Java堆空间与栈内存

堆内存为所有线程共享,空间不足时会抛出OutOfMemoryError。栈空间为线程独享大小可调整,空间不足时会抛出StackOverflowError。

堆空间

堆内存为所有线程共享,空间不足时会抛出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-Heap-Stack-Memory

程序运行,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。