Java對象什麼時候死亡_以及_finalize()方法的執行過程

一. 問題背景

遇到一個面試題“GC的兩種判定方法(如何判斷一個對象是否存活?)”,其中涉及引用計數法和可達性算法,而當一個對象經過可達性分析,會涉及兩次標記,其中又涉及finalize()。因此今天瞭解一下Java對象什麼時候死亡以及finalize()方法的執行過程。

此筆記僅供自己參考,如有錯誤請指正

參考自:JAVA中對象什麼時候死亡以及什麼時候執行finalize()方法

二. 儲備知識

2.1 引用

jdk1.2後,Java將引用分爲強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)

2.1.1 強引用Strong Reference

類似Object obj = new Object();這類引用,只要引用還在,垃圾收集器就不會回收掉被引用的對象(即new出來的Object)

2.1.2 軟引用Soft Reference

在拋出內存溢出異常之前,會把軟引用引用的對象列入回收範圍之中進行第二次回收。如果這次回收還沒有足夠的內存,纔會拋出內存溢出異常。

總結:經過一次垃圾回收後,若還是內存不足,則回收掉軟引用關聯的對象

2.1.3 弱引用Weak Reference

只要進行垃圾回收,就會回收掉引用關聯的對象

2.1.4 虛引用Phantom Reference

不會對生存時間構成影響,唯一目的是對象被回收時收到一個系統通知

2.2 可達性算法(引用鏈法)

從一個被稱爲GC Root的對象開始向下搜索,一個對象到GC Root沒有任何引用鏈相連時,則說明此對象不可用。

2.3 哪些對象可以作爲GC Root

  • 虛擬機棧中引用的對象(是對象,不是引用)
  • 方法區靜態屬性引用的對象
  • 方法區常量池引用的對象
  • 本地方法棧JNI引用的對象

2.4 finalize()相關的知識

2.4.1 finalize()來自哪裏

finalize()是Object類裏面的方法,而每一個對象都是Object類的子類,因此每一個對象都有finalize()方法(自繼承得來的)。

2.4.2 finalize()的作用

用來關閉某些資源(比如數據庫連接,IO流等等)。用法是重寫finalize()方法即可(在方法體裏面進行關閉數據庫連接,關閉IO流等等)。

三. 對象的復活(對象什麼時候死亡)以及finalize()的執行過程

  • 即使對象在可達性算法中被判爲不可達,但並非是真正死了,此時它們處於“緩刑階段”。要真正死亡需要經理2次標記過程。

  • 如果對象經過可達性分析後沒有與GC Root相連的引用鏈,那麼它將被第一次標記 並 進行一次篩選

  • 篩選的條件是:是否有必要執行finalize()方法

  • 當對象沒有覆蓋finalize()方法 或 finalize()方法已經被虛擬機調用過,虛擬機將這2中情況視爲“沒有必要執行

  • 如果對象被判定爲有必要執行finalize()方法,那麼此對象將被加入到一個叫F-Queue的隊列之中,並且稍後由一個虛擬機自行創建的低優先級FinalizeHandler線程執行它

  • 這裏的執行是指虛擬機觸發這個方法,但不承諾會等待它運行結束。這樣做的原因是如果一個對象在finalize()方法中執行緩慢,或者發生死循環,將很可能導致F-Queue隊列中其他對象永久處於等待,甚至導致整個內存回收系統崩潰。

  • finalize()方法是對象逃脫死亡的最後一次機會。稍後GC將對F-Queue的對象進行第二次小規模的標記。如果對象要在finalize()方法中拯救自己——只需重新與引用鏈上的任何一個對象建立關聯即可。譬如把自己賦值給類變量或者對象的成員變量,那麼在第二次標記時(GC會對F-Queue隊列中的對象進行第二次標記)它將被移除出“即將回收”的集合。如果對象此時還沒有逃脫,那麼基本上它就真的被回收了。

總結:finalize並不是一定要執行,它只能執行1次或0次。如果它能在finalize()中建立對象關聯,則當前對象可以復活一次。

過程如下圖:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章