Skip to main content

Java堆空间与栈的对比 - Java中的内存分配

作者:Pankaj

许多 Java 与Java EE 的书籍和教程中都会提到堆内存与栈内存,但它们很少从程序的角度出发,完整地解释这二者。

Java 堆空间

Java 在运行时,会使用堆空间为对象和 JRE 类分配内存。所有的对象都创建在在堆空间中。此外,堆内存会运行垃圾回收,删除没有被引用的对象,释放它们占用的内存。在堆空间中创建的所有对象都有全局访问权限,可以在应用程序的任何位置引用。

Java 栈内存

Java 栈内存用于执行线程。栈内存中包含用于特定方法的短期值,以及方法中对堆内其它对象的引用。堆内存中的引用始终为 LIFO(先进后出)的顺序。当调用一个方法时,会在栈内存中创建一个新模块,用来存储方法的本地原始值和对其它对象的引用。当方法调用结束后,这个模块会变成未使用状态,且可供下一个方法使用。与堆内存相比,栈内存非常小。

Java 程序中的堆内存和栈内存

让我们通过一个简单的程序,理解堆内存与栈内存的使用。

Let’s understand the Heap and Stack memory usage with a simple program.

package com.journaldev.test;

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

}

以下图片显示了上述程序的堆内存与栈内存,以及它们如何储存原始值、对象和引用变量。

下面来看看程序执行的步骤。

  • 在运行程序的瞬间,堆空间中会加载所有的运行时类(Runtime class)。当运行至第一行的 main()方法时,Java Runtime 就会创建栈内存,供 main()方法线程使用。
  • 第二行,我们创建了本地原始变量,它就会储存在 main()方法的栈内存中。
  • 在第三行中,我们创建了一个对象。对象本身会储存在堆内存中,而栈内存中包含的是对该对象的引用。我们在第四行创建 Memory 对象的过程也与之类似。
  • 当我们在第五行调用 foo()方法时,一个模块会创建在堆的顶部,用于 foo()方法。由于 Java 是值传递,第六行会在 foo() 的栈模块中创建一个新的对对象的引用。
  • 第七行创建了一个新的字符串,这个字符串将会储存在堆空间内的字符串池中,然后 foo() 方法的栈空间中将会储存这个字符串的引用。
  • foo() 方法在第八行终止。此时,栈空间中分配给 foo()的内存就会被释放。
  • 在第九行,main() 方法终止,为它创建的栈内存随之消失。同时,整个程序在这行运行完毕,Java Runtime 就会释放所有内存,终止程序的执行。

Java 堆空间与栈内存的区别

根据以上例子与解释,我们可以总结出以下堆空间与栈内存的区别。

堆内存可用于应用程序的所有部分,而栈内存仅用于单个线程的执行。 当创建对象时,这个对象只会储存在堆空间中,栈内存只储存对该对象的引用。栈内存只包含本地原始值,和堆空间中对象的引用变量。 堆中存储的对象全局可用,而栈内存无法被其它线程访问。 栈中的内存管理遵循 LIFO(先进后出)原则。而因为堆内存全局可用,其内存管理更为复杂。堆内存被分为年轻代(Yound-Generation)与老年代(Old-Generation)等等。

栈内存是短暂的,而堆内存从程序开始执行到执行结束都一直存在。 我们可以使用 JVM 的 -Xms-Xmx 选项设置堆内存的初始与最大值。我们还可以使用-Xss 设置栈内存大小。 如果栈内存满了,Java runtime 会抛出 java.lang.StackOverFlowError,而如果堆内存满了,则会抛出 java.lang.OutOfMemoryError: Java Heap Space 错误。 与堆内存相比,栈内存非常小。此外,由于栈内存实行较简单的内存分配(先进后出),与堆内存相比速度更快。

这就是 Java 堆空间与 Java 栈内存的在应用中的区别。希望本文能让您更清楚地了解 Java 程序执行时的内存分配。参考: https://en.wikipedia.org/wiki/Java_memory_model https://blogs.oracle.com/jonthecollector/presenting-the-permanent-generation