您好,登錄后才能下訂單哦!
這篇文章主要介紹Java中回收對象標記和對象二次標記的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
一、對象的標記
1、什么是標記?怎么標記?
第一個問題相信大家都知道,標記就是對一些已死的對象打上記號,方便垃圾收集器的清理。 至于怎么標記,一般有兩種方法:引用計數和可達性分析。
引用計數實現起來比較簡單,就是給對象添加一個引用計數器,每當有一個地方引用它時就加1,引用失效時就減1,當計數器為0的時候就標記為可回收。這種判斷效率很高,但是很多主流的虛擬機并沒有采用這種方法,主要是因為它很難解決幾個對象之間循環引用的問題,雖然不怎么用了,但還是值得我們學習!
public class Test { private Object obj; Public static void main(){ Test t1=new Test(); Test t2=new Test(); t1.obj=t2; t2.obj=t1; t1=null; t2=null; //如果對象在這行發生gc,那么t1和t2對象是否能被回收 System.gc(); } }
可達性分析的基本思路就是:通過將一些稱為"GC Roots"的對象作為起始點,從這些節點開始搜索,搜索和該節點發生直接或者間接引用關系的對象,將這些對象以鏈的形式組合起來,形成一張“關系網”,又叫做引用鏈。最后垃圾收集器就回收一些不在這張關系網上的對象。如圖:
連接GC Roots對象的object是確定還存活的對象,而右邊的die obj由于和GCROOTS沒有關系,所以會標記為可回收的對象。目前主流的商用虛擬機用的都是類似的方法。那什么對象才能作為“GC Roots”呢?在java中,有四種對象可以作為“GC Roots”
1:棧幀(第一章的名詞)中的引用對象。(棧中的)
2:靜態屬性引用的對象。(方法區中的)
3:常量引用的對象。(方法區中的)
4:本地方法棧中JNI引用的對象。(本地方法棧中的)
二、對象的二次回收
說過對象的標記,但是不是被標記了就肯定會被回收呢?不知道小伙伴們記不記得Object類有一個finalize()方法,所有類都繼承了Object類,因此也默認實現了這個方法。
finalize的工作原理應該是這樣的:一旦垃圾收集器準備好釋放對象占用的存儲空間,它首先調用finalize(),而且只有在下一次垃圾收集過程中,才會真正回收對象的內存.所以如果使用finalize(),就可以在垃圾收集期間進行一些重要的清除或清掃工作.
finalize()在什么時候被調用?
有三種情況
1.所有對象被Garbage Collection時自動調用,比如運行System.gc()的時候.
2.程序退出時為每個對象調用一次finalize方法。
3.顯式的調用finalize方法
這個方法的用途就是:在該對象被回收之前,該對象的finalize()方法會被調用。這里的回收之前指的就是被標記之后,問題就出在這里,有沒有一種情況就是原本一個對象開始不再上一章所講的“關系網”(引用鏈)中,但是當開發者重寫了finalize()后,并且將該對象重新加入到了“關系網”中,也就是說該對象對我們還有用,不應該被回收,但是已經被標記啦,怎么辦呢?
針對這個問題,虛擬機的做法是進行兩次標記,即第一次標記不在“關系網”中的對象。第二次的話就要先判斷該對象有沒有實現finalize()方法了,如果沒有實現就直接判斷該對象可回收;如果實現了就會先放在一個隊列中,并由虛擬機建立的一個低優先級的線程去執行它,隨后就會進行第二次的小規模標記,在這次被標記的對象就會真正的被回收了。
總結:簡單說,對象先進行第一次標記,在下一次GC之前會執行對象的finalize()方法。在執行finalize()方法的時候判斷對象是否實現了finalize()方法,沒有實現直接清除;實現了,將對象放在一個隊列中執行finalize方法,進行第二次標記
在java根搜索算法中判斷對象的可達性,對于不可達的對象,也并不一定是必須清理。這個時候有一個緩刑期,真正的判斷一個對象死亡,至少要經過倆次標記過程:如果對象在進行根搜索后發現沒有與GC roots相關聯的引用鏈,那他將會第一次標記并且進行一次篩選,篩選的條件是此對象是否有必要執行finalize()方法,當對象沒有覆蓋finalize()方法,或者finalize()方法已經被虛擬機調用過,虛擬機將這倆種情況都視為“沒有必要執行”。
即當一個對象重寫了finalize()方法的時候,這個對象被判定為有必要執行finalize()方法,那么這個對象被放置在F-Queue隊列之中,并在稍后由一條由虛擬機自動建立的、低優先級的Finalizer線程去執行。這里所謂的執行是指虛擬機會出發這個方法,但不承諾會等待它運行結束。這樣做的原因:如果一個對象在finalize()方法中執行緩慢,或者發生了死循環(極端的情況下),將可能會導致F-Queue隊列中的其他對象永久處于等待狀態,甚至導致整個內存回收系統崩潰。finalize()方法是對象逃脫死亡命運的最后一次機會,稍后GC將對F-Queue中的對象進行第二次小規模的標記,如果對象要在finalize()中成功拯救自己----只要重新與引用鏈上的任何建立關聯即可,那么在第二次標記時它將會被移出“即將回收”的集合;如果對象這時候沒有逃脫,就會被回收。代碼示例:參考《深入理解java虛擬機》對應章節
以上是“Java中回收對象標記和對象二次標記的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。