1.C/C++
参考文章1:Windows堆和栈的比较
参考文章2:内存分配——静态存储区 栈 堆
C/C++编译的程序占用的内存分为:静态存储区,栈区,堆区,文字常量区,程序代码区。
- 静态存储区:内存在程序编译的时候已经分配好,这块内存在程序的整个运行期间都存在,主要存放静态数据,全局变量和常量。
- 栈(Stack ):在执行函数时,函数内部局部变量的存储单元都可以在栈上创建,函数执行结束时这些变量自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是容量有限。
栈内存是自动分配释放的。
int b;//系统自动在栈中为b开辟空间
在Windows系统中,栈是向低地址扩展的数据结构,是一块连续的内存区域,即栈顶的地址和栈的最大容量是系统预先定好的,如果申请的空间超过栈的剩余空间,将提示overflow(溢出).栈的速度较快,但程序员无法控制。
- 堆(Heap):也叫动态分配内存,用malloc或者new申请任意大小的内存,在适当的时候用free或者delete释放内存。动态内存的生存期可以由我们决定。堆是向高地址扩展的数据结构(Windows和Linux都一样吗?),是不连续的内存区域.这是由于系统是用链表来存储空闲内存地址的,自然是不连续的,而链表的遍历方向是从低地址到高地址。堆的大小受限于计算机系统中有效的虚拟内存。
- 堆和栈的不同:
- 碎片问题:对于堆来说,频繁的new,delete会造成内存空间的不连续,从而产生大量碎片,使程序效率降低。对于栈来说,就没有这个问题,因为栈是Last In First Out,永远都不可能有一个内存块从栈中间弹出。
- 生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向,对于栈来讲,是向着内存地址减小的方法扩展的。
- 分配方式:堆是动态分配的,没有静态分配的堆,栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca(没错,就是这么拼的,这个函数是在栈上分配空间)进行分配,但是栈的动态分配是由编译器进行释放,无需我们手动实现。
- 分配效率:栈是机器系统提供的数据结构,计算机在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,机制较复杂,如为了分配一块内存,库函数会按照一定算法,在堆内存中搜索可用的足够大小的空间,如果没有(可能是由于碎片太多),就有可能调用系统功能区增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回,显然,堆的效率要低很多。
- 文字常量区:常量字符串就是放在这里,程序结束后由系统释放
- 代码区:存放函数体的二进制代码
2.Java
参考文章:Java内存管理:深入Java内存区域
Java虚拟机在执行Java程序时,会把它所管理的内存划分为5部分:
方法区,栈,堆,本地方法栈,程序计数器。
1.程序计数器
Program Counter Register是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器完成。
Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器只会执行一条线程中的指令,因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。此内存区是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
2.Java虚拟机栈
Java Virtual Machine Stacks: 同样是线程私有的,它的生命周期与线程相同。
虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame )用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
经常有人把Java内存区划分为堆内存和栈内存,其中的栈就是虚拟机栈中的局部变量表部分。
局部变量表存放了编译期可知的各种基本数据类型(boolean,byte,char, short, int, float, long, double),对象引用。
3.本地方法栈
Native Method Stacks与虚拟机栈的作用非常相似。虚拟机栈为虚拟机执行Java方法(也就是字
节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没
有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与
虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。
4.Java堆
Java堆是被所有线程共享的内存区域,此区域唯一作用就是存放对象实例(new 出来的东西):所有的对象实例以及数组都分配在堆上.
Java堆是垃圾收集器管理的主要区域,因此很多时候也被称为”GC堆”。
从内存回收的角度看,现在收集器基本采用分代收集算法,所以Java堆还可以细分为:新生代和老年代,再细致一点的有Eden空间,From Survivor空间,To Survivor空间等。
5.方法区
Method Area与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器后的代码等数据。
运行时常量池
Runtime Constant Pool是方法区的一部分。Class文件中除了有类的版本,字段,方法,接口等描述等信息外,还有一项信息是常量池。