Java 堆空间 vs 栈 – Java 内存分配

前段時間我寫了幾篇關於Java 垃圾回收Java 是傳值呼叫的文章。之後我收到了很多電子郵件,要求解釋關於Java 堆空間Java 堆疊記憶體Java 的記憶體配置以及它們之間的差異。在 Java 和 Java EE 書籍和教程中,你會看到很多有關堆和疊記憶體的參考,但很少有完整的解釋,即以程式的角度來說堆和疊記憶體是什麼。

Java 堆空間

Java 堆空間是由 Java 執行時分配給物件和 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

}

下圖顯示了堆和堆疊記憶體與上述程式的相關性,以及它們如何用於存儲基本值、物件和引用變數。讓我們瀏覽一下程式執行的步驟。

  • 當我們運行程式時,它將所有運行時類加載到堆空間中。當在第一行找到 main() 方法時,Java Runtime 將創建堆疊記憶體供 main() 方法線程使用。
  • 我們在第2行創建了一個原始的局部變量,因此它被創建並存儲在main()方法的堆棧內存中。
  • 由於我們在第3行創建了一個對象,它被創建在堆內存中,並且堆棧內存中包含對它的引用。當我們在第4行創建Memory對象時,也發生了類似的過程。
  • 現在,當我們在第5行調用foo()方法時,一個堆棧頂部的塊被創建,供foo()方法使用。由於Java是按值傳遞,因此在第6行中,在foo()堆棧塊中創建了一個新的對象引用。
  • A string is created in the 7th line, it goes in the String Pool in the heap space and a reference is created in the foo() stack space for it.
  • foo()方法在第8行終止,此時在堆棧中為foo()分配的內存塊變為空閒。
  • 在第9行,main()方法終止,並且為main()方法創建的堆棧內存被銷毀。同時,程序在此行結束,因此Java運行時釋放所有內存並結束程序的執行。

Java堆空間和堆棧內存之間的區別

根據上述解釋,我們可以輕易得出以下堆和堆棧內存之間的區別。

  1. 堆內存被應用程序的所有部分使用,而堆棧內存僅由一個執行緒使用。
  2. 每當創建一個物件時,它總是存儲在堆空間中,而堆棧內存包含對它的引用。堆棧內存只包含本地基本變量和對堆空間中物件的引用變量。
  3. 存儲在堆中的物件是全局可訪問的,而堆棧內存無法被其他線程訪問。
  4. 堆棧的內存管理是以LIFO方式進行的,而堆內存則更加複雜,因為它是全局使用的。堆內存被劃分為年輕代、老年代等,詳細信息請參閱Java垃圾回收
  5. 堆棧內存的生命週期較短,而堆內存的生命週期從應用程序開始到結束都存在。
  6. 我們可以使用-Xms-Xmx JVM選項來定義堆內存的起始大小和最大大小。我們可以使用-Xss來定義堆棧內存的大小。
  7. 當堆棧內存滿時,Java運行時會拋出java.lang.StackOverFlowError異常,而當堆內存滿時,則會拋出java.lang.OutOfMemoryError: Java Heap Space錯誤。
  8. 與堆內存相比,堆棧內存的大小非常小。由於在內存分配(LIFO)方面的簡單性,堆棧內存比堆內存更快。

這就是有關Java應用程序中的Java堆空間與堆棧內存的全部內容,我希望它能解除您對Java程序執行時的內存分配的疑慮。

參考: https://zh.wikipedia.org/wiki/Java%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B.

Source:
https://www.digitalocean.com/community/tutorials/java-heap-space-vs-stack-memory