菜鳥學Java(二十三)——Java記憶體分析

劉水鏡發表於2014-11-30

我們常說的Java記憶體主要分為四大塊(暫存器不在考慮之內,我們無法用程式碼來操控它):stack(棧)、heap(堆)、data segment(資料區)、code segment(程式碼區)。它們的主要用途如下圖所示:




而在上面四個當中,我們經常談論的是右邊那兩個傢伙——stack和heap。今天我們就來聊聊Java程式碼在執行的過程中,在stack和heap中到底是什麼樣子的吧。


我們先看下面一段程式碼:

 

    public static void main(String[] args) {
        TestReference testReference = new TestReference();
        int age = 1;
        Person xiaoqiang = new Person("小強", 21);
        Person xiaoming = new Person("小明", 22);
        
        testReference.selfPlus(age);
        System.out.println("age經過selfPlus方法的處理後為:" + age);
        
        testReference.changeName(xiaoqiang);
        System.out.println("小強經過changeName方法的處理後的名字為:" + xiaoqiang.getName());
        
        testReference.changeAge(xiaoming);
        System.out.println("小明經過changeAge方法的處理後的年齡為:" + xiaoming.getAge());
        
    }
    
    public void selfPlus(int i) {
        i = i + 1;
    }
    
    public void changeName(Person person) {
        person = new Person("小剛");
    }
    
    public void changeAge(Person person) {
        person.setAge(25);
    }

 



執行完以上程式碼,會列印出什麼內容呢?如果你Java基礎還可以,那麼很容易就能知道會輸出什麼內容,想要知道以上程式碼會列印什麼內容,需要你明白Java程式碼在stack和heap中是怎麼工作的。下面我們結合幾張圖來看看:

 

        int i = 1;
        Person xiaoqiang = new Person("小強", 21);
        Person xiaoming = new Person("小明", 22);

 


當我們執行完上面三行程式碼時,記憶體中的情況如下圖所示:



我們知道stack是用來存放變數的,所以age、xiaoqiang和xiaoming三個變數會被存放到stack裡,而age又是int型別,所以它的值也會被存放在stack裡面。xiaoqiang和xiaoming為Person型別的引用變數,所以stack只會存放它們的一個引用,也就是對應物件的記憶體地址,而它們真正的內容被存放在了heap裡面。


當執行到testReference.selfPlus(i);時,selfPlus(int i)方法被呼叫,此時會在stack中為形參I開闢一塊記憶體空間,並將其值設定為1,此時記憶體中的情況如下:


因為呼叫selfPlus(int i)方法時,將age作為形參傳遞給該方法,相當於將age的值複製一份給i,所以現在i的值為1,接著執行i = i + 1;此時i的值被修改為2,如圖:


此時被修改的只是age的副本,而並非age本身,所以age仍為1,當selfPlus(int i)方法被執行完,i被銷燬,age不變。接下來看xiaoqiang,當呼叫changeName(Person person)方法時,會在stack中為person分配一塊空間,裡面存放的是xiaoqiang指向的地址,如圖:


然後執行到person = new Person("小剛");時,由於又new了一個Person物件,所以在堆中會新建一個person,姓名叫小剛,並且會將person執行小剛的地址,如圖:

此時,person指向的物件由小強變成了小剛,但是xiaoqiang所指向的物件仍然是小強,並沒有發生任何變化。當changeName方法執行完以後,person被銷燬,而小剛也會因為沒有任何物件對其進行引用,隨後被垃圾回收器回收掉。


下面到了最後一個方法了,當執行testReference.changeAge(xiaoming);時,呼叫changeAge(Person person)方法,同樣會在stack中為person分配一塊空間,這次裡面存放的是xiaoming對應的記憶體地址,如圖:



然後執行person.setAge(25);,此時person指向的物件為小明,所以在執行setAge方法時,修改的就是heap中小明的屬性值,此時小明的年齡被修改為25,。方法執行完畢,person被銷燬,記憶體中最終結果如下:


記憶體中的最終情況就如上圖所示,當然當main方法執行完以後,之前建立的所有物件都會被銷燬。OK,每天上班做專案的你是不是把一些基礎的東西忘了呢?如果是的話就趕快來補一補吧,好處還是很多的。



相關文章