a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 181|回复: 1

[专业语言] JAVA认证:JAVA语言中内存分配策略里的堆与栈

[复制链接]
发表于 2012-8-4 12:44:44 | 显示全部楼层 |阅读模式
在JAVA说话里良多人理解不了内存分配策略中的堆与栈,小编在这里为巨匠供给这篇文章,但愿对巨匠有用。         按照编译事理的不雅概念,轨范运行时的内存分配有三种策略,分袂是静态的,栈式的,和堆式的.0 ]1 D9 x4 F4 v2 K, D0 m) O
  静态存储分配是指在编译时就能确定每个数据方针在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求轨范代码中不许可有可变数据结构(好比可变数组)的存在,也不许可有嵌套或者递归的结构呈现,因为它们城市导致编译轨范无法计较切确的存储空间需求.
  a8 R, T2 S( x- v7 u$ C  栈式存储分配也可称为动态存储分配,是由一个近似于仓库的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,轨范对数据区的需求在编译时是完全未知的,只有到运行的时辰才能够知道,可是划定在运行中进入一个轨范模块时,必需知道该轨范模块所需的数据区巨细才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照前进前辈后出的原则进行分配。* `8 y! _4 u8 ]- n
  静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的进口处必需知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块进口处都无法确定存储要求的数据结构的内存分配,好比可变长度串和对象实例.堆由年夜片的可操作块或余暇块组成,堆中的内存可以按照肆意挨次分配和释放.5 m2 Q1 r6 T1 H" F% ~" Z
堆和栈的斗劲 + T  R& H8 t' n# _" j
  膳缦沔的界说年夜编译事理的教材中总结而来,除静态存储分配之外,都显得很机械和难以理解,下面撇开静态存储分配,集中斗劲堆和栈:% l# X  i' j0 M' \, U$ a
  年夜堆和栈的功能和浸染来通俗的斗劲,堆首要用来存放对象的,栈主若是用来执行轨范的.而这种分歧又主若是因为堆和栈的特点抉择的:
& f% x  ?( M3 c) p- k/ a3 X* D, N  在编程中,例如C/C++中,所有的体例挪用都是经由过程栈来进行的,所有的局部变量,形式参数都是年夜栈平分配内存空间的。现实上也不是什么分配,只是年夜栈顶 向上用就行,就仿佛工场中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到下班具的位置,你所要做的只是把工具放下来就行.退出函数的时辰,改削栈指针就可以把栈中的内容销毁.这样的模式速度最快, 当然要用来运行轨范了.需要注重的是,在分配的时辰,好比为一个即将要挪用的轨范模块分配数据区时,应事先知道这个数据区的巨细,也就说是虽然分配是在程 序运行时进行的,可是分配的巨细若干好多是确定的,不变的,而这个”巨细若干好多”是在编译时确定的,不是在运行时.
6 `# i% E: _  @. ~1 _. n  堆是应用轨范在运行的时辰请求操作系统分配给自己内存,因为年夜操作系统打点的内存分配,所以在分配和销毁时都要占用时刻,是以用堆的效率很是低.可是堆的 利益在于,编译器不必知道要年夜堆里分配若干好多存储空间,也不必知道存储的数据要在堆里勾留多长的时刻,是以,用堆保留数据时会获得更年夜的矫捷性。事实上,面 向对象的多态性,堆内存分配是必不成少的,因为多态变量所需的存储空间只有在运行时建树了对象之后才能确定.在C++中,要求建树一个对象时,只需用 new呼吁编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保留.当然,为达到这种矫捷性,必然会支出必然的价钱:在堆里分配存储空间时会花 失踪更长的时刻!这也恰是导致我们适才所说的效率低的原因,看来列宁同志说的好,人的利益往往也是人的错误谬误,人的错误谬误往往也是人的利益(晕~).
* Z4 M  ~* e# @0 G. U/ `  I+ S4 tJVM中的堆和栈 ( c# [* h4 \/ n9 E  R' Z5 ?
  JVM是基于仓库的虚拟机.JVM为每个新建树的线程都分配一个仓库.也就是说,对于一个Java轨范来说,它的运行就是经由过程对仓库的操作来完成的。仓库以帧为单元保留线程的状况。JVM对仓库只进行两种操作:以帧为单元的压栈和出栈操作。
) i3 X* ~. d6 S2 ^6 }  我们知道,某个线程正在执行的体例称为此线程的当前体例.我们可能不知道,当前体例使用的帧称为当前帧。当线程激活一个Java体例,JVM就会在线程的 Java仓库里新压入一个帧。这个帧自然成为了当前帧.在此体例执行时代,这个帧将用来保留参数,局部变量,中心计较过程和其他数据.这个帧在这里和编译 事理中的勾当记载的概念是差不多的.7 P1 K9 w! @% ?9 |
  年夜Java的这种分配机制来看,仓库又可以这样理解:仓库(Stack)是操作系统在成立某个历程时或者线程(在撑持多线程的操作系统中是线程)为这个线程成立的存储区域,该区域具有前进前辈后出的特征。
2 J: C/ ~5 R% h" ]" [' L  n  每一个Java应用都独一对应一个JVM实例,每一个实例独一对应一个堆。应用轨范在运行中所建树的所有类实例或数组都放在这个堆中,并由应用所有的线程 共享.跟C/C++分歧,Java平分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆平分配的,可是这个对象的引用却是在仓库平分配,也 就是耸ё仝成立一个对象时年夜两个处所都分配内存,在堆平分配的内存现实成立这个对象,而在仓库平分配的内存只是一个指向这个堆对象的指针(引用)而已。: B0 Y. I* |6 @0 k- \
  具体的说:6 q6 W& l5 G& p$ `4 g6 k
  栈与堆都是Java用来在Ram中存放数据的处所。与C++分歧,Java自动打点栈和堆,轨范员不能直接地设置栈或堆。
, p4 p0 j2 |, N! H5 d2 o  Java的堆是一个运行时数据区,类的对象年夜平分配空间。这些对象经由过程new、newarray、anewarray和multianewarray等指 令成立,它们不需要轨范代码来显式邓晔着。堆是由垃圾收受接管来负责的,堆的优势是可以动态地分配内存巨细,保留期也不必事先告诉编译器,因为它是在运行时动 态分配内存的,Java的垃收受接管集器会自动收走这些不再使用的数据。但错误谬误是,因为要在运行时动态分配内存,存取速度较慢。java中的对象和数组都存放在堆中。
- B9 F" R6 q; \/ i# v. z+ X9 u
% J# v3 s9 }/ B) \  T4 k6 s  栈的优势是,存取速度比堆要快,仅次于寄放器,栈数据可以共享。但错误谬误是,存在栈中的数据巨细与保留期必需是确定的,缺乏矫捷性。栈中首要存放一些根基类型的变量(,int, short, long, byte, float, double, boolean, char)和对象引用。
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:44:45 | 显示全部楼层

JAVA认证:JAVA语言中内存分配策略里的堆与栈

</p>  栈有一个很主要的非凡性,就是存在栈中的数据可以共享。假设我们同时界说:
+ P$ V0 N! C" {9 [! J% c  int a = 3;- Z' B: [% b% d/ \
  int b = 3;
/ H$ D% w1 R) r/ N. s( D  编译器先措置int a = 3;首先它会在栈中建树一个变量为a的引用,然后查找栈中是否有3这个值,如不美观没找到,就将3存放进来,然后将a指向3。接着措置int b = 3;在建树完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就呈现了a与b同时均指向3的情形。这时,如不美观再令a=4;那么编译器 会年夜头搜索栈中是否有4值,如不美观没有,则将4存放进来,并令a指向4;如不美观已经有了,则直接将a指向这个地址。是以a值的改变不会影响到b的值。要注重这 种数据的共享与两个对象的引用同时指向一个对象的这种共享是分歧的,因为这种情形a的改削并不会影响到b, 它是由编译器完成的,它有利于节约空间。而一个对象引用变量改削了这个对象的内部状况,会影响到另一个对象引用变量。
% ]  P! N; i* I5 P( C) H/ ups:关于c++的内存分配
1 Z+ ?! A7 U% I: B& e! G7 s: o! d  一个由C/C++编译的轨范占用的内存分为以下几个部门& {3 ?2 ?, \$ Y) ]
  1、栈区(stack)— 由编译器自动分潘晔着 ,存放函数的参数值,局部变量的值等。其操作体例近似于数据结构中的栈。
1 Q! M) Y: K. g& ^9 {; m- L  2、堆区(heap) — 一般由轨范员分潘晔着, 若轨范员不释放,轨范竣事时可能由OS收受接管 。注重它与数据结构中的堆是两回事,分配体例却是近似于链表,呵呵。) l" K3 g" _: Y/ F8 o
  3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 – 轨范竣事后有系统释放
3 j7 f4 M' B7 R4 |  4、文字常量区 —常量字符串就是放在这里的。 轨范竣事后由系统释放& V5 I% T" k0 @; S8 k6 g' |
  5、轨范代码区—存放函数体的二进制代码。
9 M$ t& L! D& N0 S) w+ l  二、例子轨范& C& X, O, ?5 \" [" K
  这是一个前辈写的,很是具体
7 ?! n+ x0 i( @$ C+ I1 ]  //main.cpp: n# a5 `: y1 N: ]$ N! g
  int a = 0; 全局初始化区
' \( \, B3 k4 r/ n' C* t  char *p1; 全局未初始化区) g  W; ?- O0 q- {( D/ D
  main()- G( i/ B* }# Q! F
  {+ I; n& |! b4 w" g+ T4 W1 d0 Y
  int b; 栈: P1 Q! d: ~" g: }
  char s[] = “abc”; 栈
7 S' b% m4 y' K4 }; x  char *p2; 栈( }! s: \( S* G% e
  char *p3 = “123456″; 123456在常量区,p3在栈上。
  F; \7 o  y  L1 D3 T$ M  static int c =0; 全局(静态)初始化区0 U" v; D& n$ a  o" I! f2 s
  p1 = (char *)malloc(10);
8 L3 f5 `& v/ I  p2 = (char *)malloc(20);
' S( y! P3 ~! \* k; R# |4 X  分配得来得10和20字节的区域就在堆区。. l/ j& g) [1 j; ]  _1 x
  strcpy(p1, “123456″); 123456放在常量区,编译器可能会将它与p3所指向的”123456″优化成一个处所。" R) j/ [' W/ I- P2 Z
  }
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-7 02:03 , Processed in 0.330164 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表