近期在学习JVM,看到JVM堆内存和栈内存,但是对于堆内存和栈内存的解释不是很足,现在翻译一篇博客,加以理解。
Java堆空间
Java堆空间由Java运行时用于为Objects和JRE类分配内存。每当我们创建任何对象时,它总是在堆空间中创建。
垃圾收集在堆内存上运行,以释放没有任何引用的对象使用的内存。在堆空间中创建的任何对象都具有全局访问权限,可以从应用程序的任何位置引用。
Java堆栈内存
Java栈内存用于执行线程。栈内存包含生命周期短的方法特定值及以及对从该方法引用的堆中其他对象的引用。用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
栈存储器始终以LIFO(后进先出)顺序引用。每当调用一个方法时,都会在栈内存中创建一个栈内存块,以便该方法保存本地原始值并引用该方法中的其他对象。
方法结束后,栈内存块将变为未使用状态,并可用于下一个方法。
与堆内存相比,栈内存大小非常少。
Java程序中的堆和栈内存
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
}
下图显示了栈和堆内存,参考上述程序以及它们如何用于存储基元,对象和引用变量。
让我们来完成程序的执行步骤。
- 一旦我们运行程序,它就会将所有运行时类加载到堆空间中。当在第1行找到main()方法时,Java Runtime会创建要由main()方法线程使用的栈内存。
- 我们在第2行创建局部变量,因此它被创建并存储在main()方法的栈内存中。
- 由于我们在第3行创建了一个Object,它在堆内存中创建,栈内存包含它的引用。当我们在第4行创建Memory对象时,会发生类似的过程。
- 现在,当我们在第5行调用foo()方法时,会创建栈顶部开辟一块栈空间供foo()方法使用。由于Java是按值传递的,因此在第6行的foo()栈内存块中创建了对Object的新引用。
- 在第7行创建一个字符串,它在堆空间的String pool中,并在foo()堆栈空间中为它创建一个引用。
- foo()方法在第8行终止,此时在栈中为foo()分配的内存块变为空闲。
- 在第9行中,main()方法终止,并且为main()方法创建的栈内存被销毁。此程序也在此行结束,因此Java Runtime释放所有内存并结束程序的执行。
Java堆空间和栈内存之间的区别
基于以上解释,我们可以很容易地得出Heap和Stack内存之间的以下差异。
- 堆内存由应用程序的所有部分使用,而栈内存仅由一个执行线程使用。
- 每当创建一个对象时,它总是存储在堆空间中,而栈存储器包含对它的引用。栈内存仅包含堆空间中对象的本地原始变量和引用变量。
- 存储在堆中的对象是全局可访问的,而栈内存不能被其他线程访问。
- 堆栈中的内存管理以LIFO方式完成,而在堆内存中则更复杂,因为它是全局使用的。堆内存分为Young-Generation,Old-Generation等。
- 栈内存是短暂的,而堆内存从应用程序执行的开始到结束都存在。
- 我们可以使用-Xms和-Xmx JVM选项来定义堆内存的启动大小和最大大小。我们可以使用-Xss来定义栈内存大小。
- 当堆栈内存已满时,Java运行时抛出,
java.lang.StackOverFlowError
而如果堆内存已满,则抛出java.lang.OutOfMemoryError:Java Heap Space错误。 - 与堆内存相比,栈内存大小非常小。由于内存分配(LIFO)的简单性,与堆内存相比,栈内存非常快。
就Java应用程序而言,这就是Java堆空间与栈内存的全部内容,我希望它能清除您在执行任何Java程序时对内存分配的疑虑。
PS:本文仅是翻译,本人水平有限,翻译不对的地方请多多执教,谢谢。
原文:https://www.journaldev.com/4098/java-heap-space-vs-stack-memory