1. 程式人生 > >java堆記憶體和棧記憶體的區別

java堆記憶體和棧記憶體的區別

一段時間之前,我寫了兩篇文章文章分別是Java的垃圾回收和Java的值傳遞,從那之後我收到了很多要求解釋Java堆記憶體和棧記憶體的郵件,並且要求解釋他們的異同點。

在Java中你會看到很多堆和棧記憶體的引用,JavaEE書和文章很難在程式的角度完全解釋什麼是堆什麼是棧。

Java堆記憶體

堆記憶體在Java執行時被使用來為物件和JRE類分配記憶體。不論什麼時候我們建立了物件,它將一直會在堆記憶體上建立。垃圾回收執行在堆記憶體上來釋放沒有任何引用的物件所佔的記憶體,任何在堆上被建立的物件都有一個全域性的訪問,並且可以在應用的任何位置被引用。

Java棧記憶體

Java的棧記憶體被用來執行緒的執行,他們包含生命週期很短的具體值的方法和在堆中使用這個方法物件的引用。棧記憶體是LIFO(後進先出)序列。當方法被呼叫的時候,堆記憶體中一個新的塊被建立,儲存了本地原始值和在方法中對其他物件的引用。這個方法結束之後,這個塊對其他方法就變成可用的了。棧記憶體與堆記憶體相比是非常小的。我們用下邊的例子理解堆記憶體和棧記憶體
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建立可以被main()方法執行緒使用的棧記憶體。2、當在第一行,我們建立了本地原始變數,它在main()的棧中建立和儲存。3、因為我們在第三行建立了物件,它在堆記憶體中被建立,在棧記憶體中儲存了它的引用,同樣的過程也發生在第四行我們建立Memory物件的時候。4、當在第五行我們呼叫foo()方法的時候,在堆的頂部建立了一個塊來被foo()方法使用,因為Java是值傳遞的,在第六行一個新的物件的引用在foo()方法中的棧中被建立5、在第七行一個String被建立,它在堆空間中的
String池
中執行,並且它的引用也在foo()方法的棧空間中被建立6、foo()方法在第八行結束,此時在堆中為foo()方法分配的記憶體塊可以被釋放7、在第九行,main()方法結束,棧為main()方法建立的記憶體空間可以被銷燬。同樣程式也在行結束,Java釋放了所有的記憶體,結束了程式的執行

堆記憶體和棧記憶體的區別

基於上邊的解釋我們可以很簡單的總結出堆和棧的區別:1、應用程式所有的部分都使用堆記憶體,然後棧記憶體通過一個執行緒執行來使用。2、不論物件什麼時候建立,他都會儲存在堆記憶體中,棧記憶體包含它的引用。棧記憶體只包含本地原始變數和堆中物件變數的引用。3、儲存在堆中的物件是全域性可以被訪問的,然而棧記憶體不能被其他執行緒所訪問。4、棧中的記憶體管理使用LIFO的方式完成,而堆記憶體的管理要更復雜了,因為它是全域性被訪問的。堆記憶體被分為,年輕一代,老一代等等,更多的細節請看,
這篇文章
5、棧記憶體是生命週期很短的,然而堆記憶體的生命週期從程式的執行開始到執行結束。6、我們可以使用-Xms和-Xmx JVM選項定義開始的大小和堆記憶體的最大值,我們可以使用-Xss定義棧的大小7、當棧記憶體滿的時候,Java丟擲java.lang.StackOverFlowError異常而堆記憶體滿的時候丟擲java.lang.OutOfMemoryError: Java Heap Space錯誤8、和堆記憶體比,棧記憶體要小的多,因為明確使用了記憶體分配規則(LIFO),和堆記憶體相比棧記憶體非常快。

原文地址:http://www.journaldev.com/4098/java-heap-memory-vs-stack-memory-difference