對象引用分析
對象的三種狀態:
可達的
從根節點可以觸及到這個對象
可復活的
一旦所有引用被釋放,就是可復活狀態
因為在finalize()中可能復活該對象
不可達的
在finalize()後,可能會進入不可觸及狀態
不可達的對象不可能復活
可以回收
引用標記
Java虛擬機提供自動內存管理機制。在GC中,沒用的對象,內存是要回收的。如何高效判斷對象存活是個重要的問題。
引用計數法
此算法計算對象被引用的次數。引用失效就減一。當對象引用個數為0時表示不被引用。引用計數法,失效簡單,效率高是個不錯的算法。但Java虛擬機並不使用引用計數器算法作為對象的引用標記。原因是不能處理對象互相引用問題。如A
可達性分析算法
可達性分析算法,是通過一系列被稱為"GC Roots"的對象作為起點,從起點開始的引用鏈都沒有被鏈接,表示對象是不可達的。
Java中GC Roots對象:
- 虛擬機棧中引用的對象
- 方法區類靜態屬性引用的對象
- 方法區常量引用的對象
- 本地方法棧引用的對象
支配樹
對象的引用類型
對象分為被引用和不被引用,但這並不利於描述數據的重要級別。對於不同的場景,提供最優的對象引用策略,引用類型細分是有必要的。
對於計算過程的對象,當然在運算結束之前都不應該被垃圾回收。
對於頻繁使用數據,希望不必重復讀寫,作為緩存。盡可能的不要垃圾回收。
對於不重要的對象,希望垃圾回收的時候直接回收,不用關心是否被引用。
Java中所有特殊引用類型的父類。引用類型不會被直接回收(會有特殊策略)。這些引用類型多被用於緩存,緩存的目的是對象有條件的垃圾回收。Reference並不會產生對象。對象被Reference包裝,成為引用對象。如果引用包裝的對象沒被直接使用(即強引用),也不會被GC直接垃圾回收。如果引用的對象在使用,跟普通對象沒有確保是不會被垃圾回收的。引用的多種策略是在對象沒有被使用的情況下發生的。
應用類型主要有三個方法:
- Get()獲取包裝對象
- Clear()清空
- Enquene()註冊對象被回收回調
強引用(StrongReference)
強引用是普遍引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。如下:
這是一個常見的方法片段。StrongRef是自定義類。Object o=new Object();類型的都是強引用。此時o叫實例,而不能說o是對象。實例在棧中,對象在堆中,操作實例實際上是通過實例的指針間接操作對象。多個實例可以指向同一個對象。sRef指向的對象的作用範圍只在這個方法體中。
方法執行完,生命周期結束,所以sRef指向空沒有意義,這在虛擬機中會被優化。但非局部變量,則不同。
強引用在ArrayList的實現源代碼:
ArrayList中用於保存數據的elementData數組。在調用clea()方法實際上是讓數組中每個元素指向空,並不釋放內存。對於集合的多次使用,節約內存申請和釋放,並且如果元素個數差別不大,也省去了數組動態擴張、數組復制的時間。
?註:在ArrayList類中定義了一個私有的變量elementData數組,在調用方法清空數組時可以看到為每個數組內容賦值為null。不同於elementData=null,強引用仍然存在,避免在後續調用 add()等方法添加元素時進行重新的內存分配。使用如clear()方法中釋放內存的方法對數組中存放的引用類型特別適用,這樣就可以及時釋放內存。
-
軟引用(SoftReference)
軟引用多用在高效緩存,不會輕易被垃圾回收。軟引用內存回收發生在,GC完成後內存還不足的情況。保證回收發生在拋出OutOfMemoryError之後。一般來講,是二次GC。如果內存充足,軟引用是不會被垃圾回收的。
-
弱引用(WeakReference)
弱引用對象在垃圾回收時會被回收。
用弱引用實現的API
虛引用(PhantomReference)
"虛引用"顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定對象的生命周期。如果一個對象僅持有虛引用,那麽它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。
虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之 關聯的引用隊列中。
其它API
ReferenceQueue,一種當 weak, soft, phantom的referent被GC回收後,提供事件回調的接口。需要在實例化三大reference時,通過構造函數傳入,phantom reference是強制需要傳入的,weak和soft可不傳。
回調過程:
GC回收referent後(phantom是在回收前,finalize後),將reference enqueue到RQ中,程序通過調用RQ的remove方法來感知reference被GC回收的事件。
remove方法是阻塞的,當沒有referent被回收時(GC未調用enqueue),remove方法會一直掛起線程,當有referent被回收時,該方法返回 referent對應的reference對象。
同樣,RQ也提供了一個非阻塞的方法 poll,但這樣就做不到實時回調了。
對象引用分析