堆空间
堆内存为线程共享,空间不足时抛出OutOfMemoryError。
堆区域表示运行时数据区域,为所有类实例和阵列分配内存,虚拟机启动期间创建。
对象堆存储由JVM GC管理,堆大小可以固定,也可以是动态的(基于系统配置),堆内存不必是连续的。
Java虚拟机实现允许对堆初始大小进行控制,动态扩展或收缩。
在堆空间中创建的对象具有全局访问权限,可从应用程序任何位置引用。
栈内存
栈空间大小可以调整,空间不足时抛出StackOverflowError。
每个JVM线程都有一个与线程同时创建的私有栈空间,栈存储所有帧,帧用于存储数据和部分结果,执行动态链接,并返回方法值及调度异常。
保存局部变量和部分结果,并在方法调用和返回中起作用。
栈永远不会被直接操作,除push和pop帧外,帧可以从堆中分配。
与堆类似,栈内存不需要是连续的。
规范允许栈可以是固定或动态的,如果固定大小则可在创建栈时,独立地选择每个栈的大小。
Java栈内存用于执行线程,它们包含方法中特定类型的值,及方法对堆中对象的引用,特定值的生命很短。
堆栈始终以LIFO(后进先出)顺序引用。
调用一个方法时,会在栈内存中创建一个新块,以保存方法中的本地原始值和该方法中的对某些对象的引用。
方法调用结束后,块将变为未使用状态,供下一个方法使用。
与堆内存相比,栈内存占空间非常少。
示例
示例程序说明堆和栈的内存使用情况:
package com.xieyonghui.mt;
public class Memory {
public static void main(String[] args) { // Line 1
int i=1; // Line 2
Object obj = new Object(); // Line 3
Memory mem = new Memory(); // Line 4
mem.foo(obj); // Line 5
} // Line 9
private void foo(Object param) { // Line 6
String str = param.toString(); Line 7
System.out.println(str);
} // Line 8
}
图中为栈空间和堆内存,参考上述程序,观察它们如何存储原始类型、对象和引用。
程序运行,java类加载器会将所有运行时类加载到堆空间中。
第1行调用main()方法时,Java Runtime会创建main()方法线程需要的栈内存。
第2行创建原始局部变量,并存储在main()方法的栈内存中。
第3行创建了一个Object,存储在堆内存中,栈内存包含对它的引用。
第4行创建Memory对象时,会发生类似的过程。
第5行调用foo()方法时,在栈的顶部创建块,以供foo()方法使用。
第6行的foo()栈块中创建了对Object的新引用。
第7行创建一个字符串,存储在堆空间的String Pool中,在foo()栈空间中有一个它的引用。
第8行foo()方法终止,在栈中为foo()分配的内存块变为空闲。
第9行中,main()方法终止,为main()方法创建的栈内存会被销毁。
程序结束,Java Runtime释放所有内存并结束程序执行。
堆空间与栈内存区别
堆内存由应用程序的所有部分使用,栈内存仅由一个执行线程使用。
创建一个对象时,总是在堆空间中,栈包含对它的引用。栈内存仅包含本地原始类型变量和堆空间中对象的引用。
存储在堆中的对象是全局可访问的,栈内存不能被其他线程访问。
栈中内存管理以LIFO方式进行,堆管理很复杂。
栈内存是短暂的,而堆内存从应用程序执行的开始到结束都存在。
堆使用-Xms和-Xmx 定义大小,栈使用-Xss来定内存大小。
栈内存满时抛出java.lang.StackOverFlowError,堆内存满时抛出java.lang.OutOfMemoryError。