在Java
中,您会看到大量关于堆内存和栈内存的引用,Java
把内存分成两种,一种叫做栈内存,一种叫做堆内存。
Java 堆内存(Heap Memory)
Java
运行时会在堆内存中将内存分配给对象和子类。每当我们创建任何对象时,它总是在堆空间中创建的。
垃圾收集在堆内存上运行,以释放没有任何引用的对象使用的内存。堆空间中创建的任何对象都具有全局访问权限,可以从应用程序的任何位置引用。
Java 栈内存(Stack Memory)
Java栈内存用于执行线程。它们包含短期存活的方法的特定值,以及堆中方法对其他对象的引用。
栈内存总是按后进先出顺序引用。每当调用一个方法时,都会在栈内存中创建一个新块,以便该方法保存本地基元值以及该方法对其他对象的引用。
一旦方法结束,该块就不再使用,就可用于下一个方法。与堆内存相比,栈内存非常小。
Java 程序中的堆内存和栈内存
1 | public class Memory { |
下图展示了上述程序的堆栈内存的引用
程序执行的过程如下:
- 一旦程序开始运行,它将会加载所有运行时类到堆内存,第1行发现了main方法,Java Runtime 创建了栈内存给main方法线程使用。
- 第2行,我们创建了int型本地变量,它被创建并存储在栈内存的main方法里。
- 第3行,我们创建一个对象,它创建在堆内存,栈内存保存它的引用。Memory 对象类似。
- 第5行,我们调用foo方法,在栈内存头部创建一个内存块给foo方法使用,既然Java通过值传递,第6行,对象的新引用将在foo方法的栈内存块内创建。
- 第7行,创建了字符串,它将会进入堆中的字符串常量池,foo方法的栈空间内将创建一个它的引用。
- foo方法在第8行终止。栈中分配的内存块将会被释放。
- 在第9行,main方法终止,分配给main方法的栈内存将会被销毁。同时,程序也在这一行结束,因此 Java Runtime将会释放所有内存,然后结束程序的执行。
堆内存和栈内存的区别
- 应用程序的所有地方都会使用堆内存,而只有线程的执行会使用栈内存。
- 当一个对象创建时,它存放在堆内存中,引用放在栈内存中。栈内存只包含本地原始变量和引用变量。
- 存放在堆内存的对象是全局的,而栈内存中的不能被其他线程获取。
- 栈中的内存管理是以后进先出的方式完成的,而堆内存中的内存管理更复杂,因为它是全局使用的。堆内存分为新生代、老年代等,更多信息可以参考Java 垃圾回收。
- 栈内存是短暂存在的,而堆内存从应用程序执行的开始到结束都是存在的。
- 我们可以使用
-Xms
和-Xmx
来定义堆内存启动时的大小和内存最大值。可以使用-Xss
去定义栈内存大小。 - 当栈内存被填满的时候,将会抛出
java.lang.StackOverFlowError
,然而,当堆内存被填满的时候,它会抛出java.lang.OutOfMemoryError: Java Heap Space
错误。 - 和堆内存大小相比,栈内存大小非常小。由于内存分配(LIFO)的简单性,与堆内存相比,栈内存非常快。