JAVA中内存分配策略里的堆与栈

日期: 2012-05-09 来源:TechTarget中国 英文

  按照编译事理的分配概念,运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的。

  静态存储分配是指在编译时就能确定每个数据方针在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间。这种分配策略要求代码中不许可有可变数据结构(好比可变数组)的存在,也不许可有嵌套或者递归的结构呈现,因为它们城市导致编译轨范无法计较切确的存储空间需求。

  栈式存储分配也可称为动态存储分配,是由一个近似于仓库的运行栈来实现的。和静态存储分配相反,在栈式存储方案中,对数据区的需求在编译时是完全未知的,只有到运行的时辰才能够知道,可是划定在运行中进入一个轨范模块时,必需知道该轨范模块所需的数据区巨细才能够为其分配内存。和我们在数据结构所熟知的栈一样,栈式存储分配按照前进前辈后出的原则进行分配。

  静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的进口处必需知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块进口处都无法确定存储要求的数据结构的内存分配,好比可变长度串和对象实例。堆由年夜片的可操作块或余暇块组成,堆中的内存可以按照肆意挨次分配和释放。

  堆和栈

  在编程中,例如C/C++中,所有的体例挪用都是经由过程栈来进行的,所有的局部变量,形式参数都是年夜栈平分配内存空间的。现实上也不是什么分配,只是年夜栈顶 向上用就行,就仿佛工场中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到下班具的位置,你所要做的只是把工具放下来就行。退出函数的时辰,改削栈指针就可以把栈中的内容销毁。这样的模式速度最快, 当然要用来运行轨范了。需要注重的是,在分配的时辰,好比为一个即将要挪用的轨范模块分配数据区时,应事先知道这个数据区的巨细,也就说是虽然分配是在程 序运行时进行的,可是分配的巨细若干好多是确定的,不变的,而这个“巨细若干好多”是在编译时确定的,不是在运行时。

  堆是应用轨范在运行的时辰请求操作系统分配给自己内存,因为年夜操作系统打点的内存分配,所以在分配和销毁时都要占用时刻,是以用堆的效率很是低。可是堆的 利益在于,编译器不必知道要年夜堆里分配若干好多存储空间,也不必知道存储的数据要在堆里勾留多长的时刻,是以,用堆保留数据时会获得更年夜的矫捷性。事实上,面 向对象的多态性,堆内存分配是必不成少的,因为多态变量所需的存储空间只有在运行时建树了对象之后才能确定。在C++中,要求建树一个对象时,只需用new呼吁编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保留。当然,为达到这种矫捷性,必然会支出必然的价钱:在堆里分配存储空间时会花 失踪更长的时刻!这也恰是导致我们适才所说的效率低的原因,看来列宁同志说的好,人的利益往往也是人的错误谬误,人的错误谬误往往也是人的利益。

  JVM中的堆和栈

  JVM是基于仓库的虚拟机.JVM为每个新建树的线程都分配一个仓库。也就是说,对于一个Java轨范来说,它的运行就是经由过程对仓库的操作来完成的。仓库以帧为单元保留线程的状况。JVM对仓库只进行两种操作:以帧为单元的压栈和出栈操作。

  我们知道,某个线程正在执行的体例称为此线程的当前体例。我们可能不知道,当前体例使用的帧称为当前帧。当线程激活一个Java体例,JVM就会在线程的 Java仓库里新压入一个帧。这个帧自然成为了当前帧。在此体例执行时代,这个帧将用来保留参数,局部变量,中心计较过程和其他数据。这个帧在这里和编译 事理中的勾当记载的概念是差不多的。

  从Java的这种分配机制来看,仓库又可以这样理解:仓库(Stack)是操作系统在成立某个历程时或者线程(在撑持多线程的操作系统中是线程)为这个线程成立的存储区域,该区域具有前进前辈后出的特征。

  每一个Java应用都独一对应一个JVM实例,每一个实例独一对应一个堆。应用轨范在运行中所建树的所有类实例或数组都放在这个堆中,并由应用所有的线程 共享。跟C/C++分歧,Java平分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆平分配的,可是这个对象的引用却是在仓库平分配,也 就是耸ё仝成立一个对象时年夜两个处所都分配内存,在堆平分配的内存现实成立这个对象,而在仓库平分配的内存只是一个指向这个堆对象的指针(引用)而已。

  具体的说:

  栈与堆都是Java用来在Ram中存放数据的处所。与C++分歧,Java自动打点栈和堆,轨范员不能直接地设置栈或堆。

  Java的堆是一个运行时数据区,类的对象年夜平分配空间。这些对象经由过程new、newarray、anewarray和multianewarray等指令成立,它们不需要轨范代码来显式邓晔着。堆是由垃圾收受接管来负责的,堆的优势是可以动态地分配内存巨细,保留期也不必事先告诉编译器,因为它是在运行时动 态分配内存的,Java的垃收受接管集器会自动收走这些不再使用的数据。但错误谬误是,因为要在运行时动态分配内存,存取速度较慢。java中的对象和数组都存放在堆中。

  栈的优势是,存取速度比堆要快,仅次于寄放器,栈数据可以共享。但错误谬误是,存在栈中的数据巨细与保留期必需是确定的,缺乏矫捷性。栈中首要存放一些根基类型的变量(,int,short,long,byte,float,double,boolean,char)和对象引用。

  栈有一个很主要的非凡性,就是存在栈中的数据可以共享。假设我们同时界说:

  int a = 3;

  int b = 3;

  编译器先措置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, 它是由编译器完成的,它有利于节约空间。而一个对象引用变量改削了这个对象的内部状况,会影响到另一个对象引用变量。

  ps:关于c++的内存分配

  一个由C/C++编译占用的内存分为以下几个部门

  1、栈区(stack)—由编译器自动分潘晔着 ,存放函数的参数值,局部变量的值等。其操作体例近似于数据结构中的栈。

  2、堆区(heap)—一般由轨范员分潘晔着, 若轨范员不释放,轨范竣事时可能由OS收受接管 。注重它与数据结构中的堆是两回事,分配体例却是近似于链表,呵呵。

  3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 – 轨范竣事后有系统释放

  4、文字常量区—常量字符串就是放在这里的。 轨范竣事后由系统释放

  5、轨范代码区—存放函数体的二进制代码。

  例子

  这是一个前辈写的,很是具体

  //main.cpp

  int a = 0; 全局初始化区

  char *p1; 全局未初始化区

  main()

  {

  int b; 栈

  char s[] = “abc”; 栈

  char *p2; 栈

  char *p3 = “123456”; 123456在常量区,p3在栈上。

  static int c =0; 全局(静态)初始化区

  p1 = (char *)malloc(10);

  p2 = (char *)malloc(20);

  分配得来得10和20字节的区域就在堆区。

  strcpy(p1,“123456”); 123456放在常量区,编译器可能会将它与p3所指向的“123456”优化成一个处所。

  }

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐