之前我寫了幾篇有關Java垃圾收集的文章之后,我收到了很多電子郵件,請求解釋Java堆空間,Java棧內存,Java中的內存分配以及它們之間的區別。
您可能在Java,Java EE書籍和教程中看到很多有關堆和變量內存的參考,但是幾乎沒有就程序而言完全解釋堆和棧的內存分配的。
Java堆空間
Java運行時使用Java堆空間為對象和JRE類分配內存。每當我們創建任何對象時,它總是在堆空間中創建。
垃圾回收在堆內存上運行以釋放沒有任何引用的對象使用的內存。在堆空間中創建的任何對象都具有訪問權限,並且可以從應用程序的任何位置進行引用。
Java棧內存
Java Stack內存用於執行線程。它們包含短期的方法特定值,以及從該方法引用的對堆中其他對象的引用。
每當調用方法時,都會在磁盤存儲中創建一個新塊,以容納該方法的本地原始值並引用該方法中的其他對象。
方法結束后,該塊將立即立即變為未使用狀態,用作下一個方法使用。
與堆內存分配,棧內存的大小要小一點。
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行中創建了一個對象,因此將在堆內存中創建該對象,並且棧內存塊包含該對象的引用。在第4行中創建內存對象時,也會發生類似的過程。
- 現在,當我們在第5行調用foo()方法時,將在棧內存頂部創建一個塊,以供foo()方法使用。由於Java是按值傳遞的,因此在第6行的foo()變量中中創建了對Object的新引用。
- 在第7行創建一個字符串,該字符串進入堆空間的“字符串池”,並在foo()最小空間中創建引用。
- foo()方法在第8行終止,這時分配給中foo()的棧內存塊被釋放。
- 在第9行中main()方法終止,並為main()方法創建的棧內存被銷毀。程序同時此行結束,因此Java Runtime釋放了所有內存並結束了程序的執行。
Java堆空間和棧內存之間的區別
根據以上解釋,我們可以輕松得出以下堆空間和棧內存的區別。
- 堆內存由應用程序的所有部分使用,而堆棧內存僅由一個執行線程使用。
- 在內存中創建對象時,它始終存儲在堆空間中,並存儲到包含該對象的引用中。內存僅包含本地原始變量和堆空間中對象的引用變量。
- 堆中存儲的對象可以分區訪問,而其他線程則不能訪問分區內存。
- 堆棧中的內存管理是以后進先出(LIFO)的方式完成的,而堆內存中的內存管理更復雜,因為它是全局使用的。
- 棧內存是短暫的,而堆空間是從應用程序執行的開始一直到結束。
- 我們可以使用-Xms和-Xmx JVM選項來定義堆空間的初始值和最大值。我們可以使用-Xss定義棧內存的大小。
- 當棧內存已滿時,Java運行時將
java.lang.StackOverFlowError
引發,而如果堆內存已滿,則將引發java.lang.OutOfMemoryError: Java Heap Space
錯誤。 - 棧內存相比於堆空間是非常小的。由於LIFO的簡單性,與堆空間相比,棧內存非常快。
就Java應用程序而言,這就是Java堆空間與棧內存的全部,我希望它能在執行任何Java程序時消除您對內存分配的疑問。
“不積跬步,無以至千里”,希望未來的你能:有夢為馬 隨處可棲!加油,少年!
關注公眾號:「Java 知己」,每天更新Java知識哦,期待你的到來!
- 發送「Group」,與 10 萬程序員一起進步。
- 發送「面試」,領取BATJ面試資料、面試視頻攻略。
- 發送「玩轉算法」,領取《玩轉算法》系列視頻教程。
- 千萬不要發送「1024」...