最近在對手機性能作了一些小概念總結,方便對於本身的我的思考java
Shallow Size:程序員
對象自己佔用的內存空間,不包含其引用的對象,但在JAVA中除基本類型外,一切均爲對象,也就是說持有的一直爲對象的引用,如String類型對象,它主要包含3個int成員(3*4B)、1個char[]成員(1*4B)以及一個對象頭(8B),儘管char[]可能指向一大塊字符,但String對象裏只有一個引用所佔4B的空間,所以String類型對象的Shallow Size就是12B+4B+8B = 24B。安全
Retained Size:ide
對象自己的Shallow Size + 對象能直接或間接訪問到的對象的Shallow Size,也就是說Retained Size就是該對象被gc以後所能回收內存的總和。性能
爲了更好地理解Retained Size,不妨以圖1加以說明,圖中每一個節點對應內存中的一個對象,但注意這裏有個特殊的節點GC Roots(詳見註解),它也是Reference chain的起點。在圖3中,從對象obj1入手,可知經過obj1能夠直接或間接訪問的對象爲藍色節點,而左圖中obj3是GC Roots可達的,也就是說斷掉obj1到obj3的Reference chain後,obj3還有GC Roots的Reference chain,於是在gc時obj1被回收也不會回收obj3,所以在左圖obj1的obj1的Retained Size = obj1 + obj2 + obj4 的shallow size,而右圖obj1的Retained Size = obj1 + obj2 + obj4 +obj3 的shallow size.總之,Retained size是一個總體度量,能反映內存結構和對象圖的依賴關係,還能夠找到根節點。測試
圖1 Retained Size示例圖this
注:GC Roots特指垃圾收集器對象,JVM GC時會回收那些不是GC roots且沒有被GC roots引用的對象。JVM對內存中對象進行回收,主要是經過GC Roots Tracing來辨別對象是否在使用,進而進行回收。也就是說經過一系列名爲「GC Roots」的對象做爲起始點,從節點向下搜索,搜索過的路徑稱之爲Reference Chain,當一個對象到GC Roots沒有任何Reference Chain相連時,則代表此對象不可用,GC時將被回收(該流程的示例圖見圖2)。spa
圖2.GC Roots Tracing回收機制.net
Heap Size: 線程
堆的大小,當資源增長,當前堆的空間不夠時,系統會增長堆的大小,若超過上限(如64M,閾值視平臺而定)則會被殺掉
Allocated:
堆中已分配的大小,即App應用實際佔用的內存大小,資源回收後,此項數據會變小。
關於gc過程,咱們又要看一個finalize方法,能夠更好的理解gc
1 java的GC只負責內存相關的清理,全部其它資源的清理必須由程序員手工完成。要否則會引發資源泄露,有可能致使程序崩潰。
2 調用GC並不保證GC實際執行。
3 finalize拋出的未捕獲異常只會致使該對象的finalize執行退出。
4 用戶能夠本身調用對象的finalize方法,可是這種調用是正常的方法調用,和對象的銷燬過程無關。
5 JVM保證在一個對象所佔用的內存被回收以前,若是它實現了finalize方法,則該方法必定會被調用。Object的默認finalize什麼都不作,爲了效率,GC能夠認爲一個什麼都不作的finalize不存在。
6 對象的finalize調用鏈和clone調用鏈同樣,必須手工構造。
如
Java代碼
對象的銷燬過程
在對象的銷燬過程當中,按照對象的finalize的執行狀況,能夠分爲如下幾種,系統會記錄對象的對應狀態:
unfinalized 沒有執行finalize,系統也不許備執行。
finalizable 能夠執行finalize了,系統會在隨後的某個時間執行finalize。
finalized 該對象的finalize已經被執行了。
GC怎麼來保持對finalizable的對象的追蹤呢。GC有一個Queue,叫作F-Queue,全部對象在變爲finalizable的時候會加入到該Queue,而後等待GC執行它的finalize方法。
這時咱們引入了對對象的另一種記錄分類,系統能夠檢查到一個對象屬於哪種。
reachable 從活動的對象引用鏈能夠到達的對象。包括全部線程當前棧的局部變量,全部的靜態變量等等。
finalizer-reachable 除了reachable外,從F-Queue能夠經過引用到達的對象。
unreachable 其它的對象。
來看看對象的狀態轉換圖。
好大,好暈,慢慢看。
1 首先,全部的對象都是從Reachable+Unfinalized走向死亡之路的。
2 當從當前活動集到對象不可達時,對象能夠從Reachable狀態變到F-Reachable或者Unreachable狀態。
3 當對象爲非Reachable+Unfinalized時,GC會把它移入F-Queue,狀態變爲F-Reachable+Finalizable。
4 好了,關鍵的來了,任什麼時候候,GC均可以從F-Queue中拿到一個Finalizable的對象,標記它爲Finalized,而後執行它的finalize方法,因爲該對象在這個線程中又可達了,因而該對象變成Reachable了(而且Finalized)。而finalize方法執行時,又有可能把其它的F-Reachable的對象變爲一個Reachable的,這個叫作對象再生。
5 當一個對象在Unreachable+Unfinalized時,若是該對象使用的是默認的Object的finalize,或者雖然重寫了,可是新的實現什麼也不幹。爲了性能,GC能夠把該對象之間變到Reclaimed狀態直接銷燬,而不用加入到F-Queue等待GC作進一步處理。
6 從狀態圖看出,無論怎麼折騰,任意一個對象的finalize只至多執行一次,一旦對象變爲Finalized,就怎麼也不會在回到F-Queue去了。固然沒有機會再執行finalize了。
7 當對象處於Unreachable+Finalized時,該對象離真正的死亡不遠了。GC能夠安全的回收該對象的內存了。進入Reclaimed。
對象重生的例子
Java代碼
期待輸出
Java代碼
可是有可能失敗,源於GC的不肯定性以及時序問題,多跑幾回應該能夠有成功的。詳細解釋見文末的參考文檔。
對象的finalize的執行順序
全部finalizable的對象的finalize的執行是不肯定的,既不肯定由哪一個線程執行,也不肯定執行的順序。
考慮如下狀況就明白爲何了,實例a,b,c是一組相互循環引用的finalizable對象。
什麼時候及如何使用finalize
從以上的分析得出,如下結論。
1 最重要的,儘可能不要用finalize,太複雜了,仍是讓系統照管比較好。能夠定義其它的方法來釋放非內存資源。
2 若是用,儘可能簡單。
3 若是用,避免對象再生,這個是本身給本身找麻煩。
4 能夠用來保護非內存資源被釋放。即便咱們定義了其它的方法來釋放非內存資源,可是其它人未必會調用該方法來釋放。在finalize裏面能夠檢查一下,若是沒有釋放就釋放好了,晚釋放總比不釋放好。
5 即便對象的finalize已經運行了,不能保證該對象被銷燬。要實現一些保證對象完全被銷燬時的動做,只能依賴於java.lang.ref裏面的類和GC交互了。
參考
關於引用類型,GC,finalize的相互交互能夠參考ReferenceQueue GC finalize Reference 測試及相關問題