Java當中的內存分配以及值傳遞問題內存解析

http://blog.sina.com.cn/s/blog_6ede15b10100mxcx.html

(注:String雖然是引用類型,但是string本身的特殊性,String的拼接會創建新對象,分配新地址,所以無法對應原來的值,string值傳遞時不改變原來的值)

首先必須說明作爲Java程序員對於內存只要有大致的瞭解就可以了,如果你對Java當中的某一個知識點在不需要分析內存分配過程的情況下可以掌握,那就大可不必去研究內存。如果你對知識點已經掌握,那麼你應該把更多的精力放在對業務邏輯的分析與設計上,這樣的話你纔可能這一行業走的更遠。 

好了廢話不多說了,下面我帶着大家先來簡單的看一下Java當中所涉及的內存分配,接着我會以講解Java當中的值傳遞問題,分析在代碼執行的過程當中內存的狀態。 

一、Java當中所涉及到的內存分類

Java當中的內存分配以及值傳遞問題內存解析

Java當中你知道這5種內存就夠用了,下面對這5種內存裏面所存放的數據做一解釋。

     棧內存:它裏面存放的是引用(也就是地址,Java當中的這個地址並非內存的物理地址,但是它通過這個地址找到它所指向地址的內容)還有就是基本類型的值以及方法的形參也是存放在棧內存當中的。

     對內存:它裏面存放的是對象引用基本類型的值(對於引用和基本類型的值什麼時候放在棧裏什麼時候放在堆裏,在後面講解Java當中的值傳遞問題,分析在代碼執行的過程當中內存的狀態的時候會說)。

     寄存器:它裏面存放的是中間運算的數字(對於這個我們可以忽略不去考慮它)。

     代碼段:顧名思義它裏面放的就是程序的代碼。

     池內存:池裏面方的是常駐內存反覆利用的數據。 

好了這就是Java當中常見內存以及它裏面所存放的數據,下面我們通過講解Java當中的值傳遞問題,分析在代碼執行的過程當中內存的狀態。 

二、Java當中的值傳遞問題以及代碼執行過程當中內存的狀態

什麼是值傳遞?

值傳遞就是Java當中參數傳遞的一種方式(而且也是唯一的一種方式,也就是說Java當中只有值傳遞),所謂參數傳遞就是在某個方法被調用的時候把一個實參傳遞給形參的過程。

下面我們通過分析下面代碼執行過稱中內存的狀態來說明Java當中的參數傳遞以及爲什麼Java當中只有值傳遞。 

代碼清單:(爲了節省空間格式不是很規範)

定義學生類:

Java當中的內存分配以及值傳遞問題內存解析
定義測試類:

Java當中的內存分配以及值傳遞問題內存解析
測試結果:

Java當中的內存分配以及值傳遞問題內存解析
爲什麼會有這樣的結果?下面我們分析一下這段代碼執行過稱當中內存的分配,相信問題將迎刃而解。

 

1、  我們運行TestPassing這類,虛擬機加載TestPassing這個類,虛擬機將這些代碼存放到代碼段當中(這裏我們就不畫出代碼段的圖示了,後面虛擬機調用任何方法(包括構造方法)都要先到代碼段中去找,但是這比較簡單也不是重點接下來的解析當中如果涉及到方法調用就不再說明了),然後虛擬機從代碼段當中找到main()方法,開始執行代碼。

此時虛擬機爲main()創建棧內存,內存分配如下

Java當中的內存分配以及值傳遞問題內存解析

2、  接着執行int age = 20;這行代碼,由於它是基本類型的局部變量所以直接把它的值20存在棧內存名字叫age,內存分配如下

     Java當中的內存分配以及值傳遞問題內存解析

 

3、  接着執行TestPassing tp = new TestPassing();這一行代碼,這句話在內存當中做了3個操作,首先TestPassing tptp是一個引用類型的變量所以給它分配一塊棧內存存放一個TestPassing對象的引用(也就是地址假設這個地址是ox 1a2b3c),接下來在堆內存創建一個TestPassing對象,接着把剛纔棧裏面tp的引用指向堆裏面的這個TestPassing對象,這行代碼的順序之所以是這樣是因爲“=”的優先級比“new”的優先級低。內存分配如下(在此只給出最終內存分配圖)

       Java當中的內存分配以及值傳遞問題內存解析

4、  接着執行這一行代碼tp.addAge(age); TestPassing對象調用addAge(int age)方法,虛擬機爲addAge(int age)方法分配一個臨時的棧內存,並且在這塊臨時棧內存當中爲addAge(int age)的形參age也分配一小塊棧內存,接着把main()當中的實參age的副本(注意是實參age的副本而不是實參age)傳給形參age,由於實參age是基本類型所以實參age的副本就是20,也就是說把20傳給形參age,此時的內存分配如下

      Java當中的內存分配以及值傳遞問題內存解析

這行代碼到此還沒有執行完,參數傳過去之後接着程序跳到被調方法當中去執行,也就是執行age++;此時操作的是形參age與實參age沒有任何關係,age++;完了之後形參age的值變成21,此時的內存分配如下

       Java當中的內存分配以及值傳遞問題內存解析

被調方法還沒結束,程序接着往下執行到方法體的結束大括號,被調方法執行完畢,同時addAge(int age)的臨時棧內存關閉。此時的內存分配如下

       Java當中的內存分配以及值傳遞問題內存解析

5、  程序接着執行System.out.println("age=" + age);(這一行代碼的內存分配過程我想沒人想讓我畫吧)這一行代碼,很清楚看上面的內存圖,也就不難理解爲什麼打印出20了。

6、  接下來程序執行到Student s1 = new Student();這一行代碼和上面TestPassing tp = newTestPassing();內存分配的過稱基本一樣,這句話也是在內存當中做了3個操作,首先Student s1s1是一個引用類型的變量所以給它分配一塊棧內存存放一個Student對象的引用(假設這個地址是ox1a2b3d),接下來在堆內存創建一個Student對象,它有一個int類型屬性age,所以在剛纔創建的對象的大塊內存當中分出一小塊來存放這個屬性,裏面存的值是0名在叫age(全局變量有默認值所以我們沒給它賦值就默認爲0),接着把剛纔棧裏面s1的引用指向堆裏面的這個Student對象。此時的內存分配如下

        Java當中的內存分配以及值傳遞問題內存解析

7、  接着程序執行s1.age = 20;這一行代碼,這行代碼將堆內存當中的Student對象的age改爲20,此時的內存分配如下

         Java當中的內存分配以及值傳遞問題內存解析

8、  接着程序執行tp.addAge(s1);這一行代碼,TestPassing對象調用addAge(Student s)方法,虛擬機爲addAge(Student s)方法分配一個臨時的棧內存,並且在這塊臨時棧內存當中爲addAge(Student s)的形參s也分配一小塊棧內存,接着把main()當中的實參s1副本傳給形參s,但是s1是引用類型它的副本就是它現在在棧內存裏面的地址,也就是說把Student的地址傳給形參s,所以形參就會根據這個地址找到Student對象,此時的內存分配如下

         Java當中的內存分配以及值傳遞問題內存解析

這行代碼到此還沒有執行完,參數傳過去之後接着程序跳到被調方法當中去執行,也就是執行s.age++;此時它操作的時是真正的Student對象,所以這行代碼執行完了之後Student對象的age屬性就變成了21,此時的內存分配如下

         Java當中的內存分配以及值傳遞問題內存解析

被調方法還沒結束,程序接着往下執行到方法體的結束大括號,被調方法執行完畢,同時addAge(Student s)的臨時棧內存關閉。此時的內存分配如下

         Java當中的內存分配以及值傳遞問題內存解析

9、  程序接着執行System.out.println("s1.age=" + s1.age);這一行代碼,很清楚看上面的內存圖,也就不難理解爲什麼打印出21了。

10、  main()結束,main()棧內存關閉,沒有任何引用指向堆內存當中的TestPassing對象和Student對象,垃圾回收器回收資源,虛擬機關閉。

 

    好了關於Java當中的內存分配以及值傳遞問題內存解析就說到這,Java當中的池內存也是一個很重要的概念,由於時間關係本次分析並未提及池內存,有時間再給大家分享。可以給大家一個思考題,如果給addAge(int age) 這個方法再加一String類型的形參也就是把這個方法改成addAge(int age, String name)並在這個方法裏面改變name的值,給Student類再加一個屬性String name,並在addAge(Student s)方法當中修改s.name的值,這樣的話String是引用類型,那麼name會怎樣變呢?

發佈了24 篇原創文章 · 獲贊 51 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章