如何判斷對象是否已死

如何確定對象是“活着”還是已經“死去”?

一、判斷對象存活的算法

1、引用計數法(未使用)

判定條件:

  • 給對象添加一個引用計數器,每當有一個地方引用它時,計數器的值加1;當引用失效時,計數器值減1;任何時刻計數器爲0的對象就是不可能再被使用的。

特點:

  • 實現簡單,判定效率也很高,在大部分情況下它都是一個不錯的算法。
  • 很難解決對象之間循環引用的問題,主流 JVM 都沒有使用。

2、可達性分析法

判定條件:

  • 通過一系列的稱爲“GC Roots”的對象作爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個對象到 GC Roots 沒有任何引用鏈相連(就是 GC Roots 到這個對象不可達)時,則證明此對象是不可用的(不代表會死)。

在 Java 語言中,可作爲 GC Roots 的對象包括下面幾種:

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象。
  • 方法區中類靜態屬性引用的對象。
  • 方法區中常量引用的對象。
  • 本地方法棧中 JNI(即一般說的 Native 方法)引用的對象。

二、生存還是死亡

1、再談引用

  無論通過引用計數法還是可達性分析算法判斷對象的引用鏈是否可達,判定對象是否存活都與引用有關。

   JDK 1.2 之後,Java 對引用的概念進行了擴充,將引用分爲強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)4種,引用強度依次減弱。

  • 強引用:指在程序代碼之中普遍存在的,類似“Object obj = new Object()”這類的引用,只要強引用在,垃圾收集器永遠不會回收調被引用的對象。
  • 軟引用:用來描述還有用但並不需要的對象。對於軟引用關聯着的對象,在系統將要發出內存溢出異常之前,將會把這些對象列進回收範圍之中進行第二次回收。如果這次回收還沒有足夠的內存,纔會拋出內存溢出異常。Java 提供了 SoftReference 類來實現軟引用。
  • 弱引用:用來描述非必需的對象,它的強度比軟引用再弱一些,被弱引用關聯的對象只能生存到一次垃圾收集發生這前。當垃圾收集器工作時,無論當前內存是否足夠,都會回調。Java 提供了 WeakReference 類來實現弱引用。
  • 虛引用:也稱爲幽靈引用或者幻影引用,是最弱的一種引用關係。一個對象是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來取得一個對象實例。爲一個對象設置虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知。Java 提供了 PhantomReference 類來實現 。

2、生存還是死亡

   即使在可達性分析法中不可達的對象,也並非是“非死不可”,這時候處於“緩刑”階段,需要以下兩次標記會能宣告一個對象死亡:

  • 如果對象在進行可達性分析後發現沒有與 GC Roots 相連接的引用鏈,那它將會被第一次標記並且進行一次篩選,篩選條件是些對象是否有必要執行 finalize() 方法。當對象沒有覆蓋 finalize() 方法,或者 finalize() 方法已經被虛擬機調用過,虛擬機將這兩種情況都視爲“沒有必要執行”。
  • 如果這個對象被判定爲有必要執行 finalize() 方法,那麼這個對象將會放置在一個叫做F-Queue的隊列中,並在稍後由一個由虛擬機自動建立的、低優先級的 Finalizer 線程去執行它。finalize() 方法是對象逃脫死亡命運的最後一次機會,稍後 GC 將對 F-Queue 中的對象進行第二次小規模的標記,如果對象要在 finalize() 中成功拯救自己——只要重新與引用鏈上的任何一個對象建立關聯即可,比如把自己(this 關鍵字)賦值給某個類變量或者對象的成員變量,那在第二次標記時它將被移除出“即將回收”的集合;如果對象這個時候還沒有逃脫,那基本上它就真的就被回收了。

任何一個對象的 finalize() 方法都只會被系統自動調用一次,如果對象面臨下一次回收,它的 finalize() 方法不會被再次執行。

3、回收方法區(1.8 之後 ?)

   永久代的垃圾回收兩部分內容:廢氣常量和無用的類。回收廢棄常量與回收 Java 堆中的對象非常類似。

   判定一個常量是否是“廢棄常量”比較簡單,而要判定一個類是否是“無用的類”的條件則相對苛刻的多:

  • 該類所有的實例都已經被回收,也就是 Java 堆中不存在該類的任何實例。
  • 加載類的 ClassLoader 已經被回收。
  • 該類對應的 java.lang.Class 對象沒有在任何地方被引用,無法在任何地方通過反射訪問類的方法。

   虛擬機可以對滿足上述 3 個條件的無用類進行回收,這裏說的僅僅是“可以”,而並不是和對象一樣,不使用了就必然會回收。是否對類進行回收,HotSpot 虛擬機提供了 -Xnoclassgc 參數進行控制,還可以使用 -verbose:class 以及 -XX:+TraceClassLoading、-XX:+TraceClassUnLoading 查看類加載和卸載信息,其中 -verbose:class 和 -XX:+TraceClassLoading 可以在 Product 版的虛擬機中使用,-XX:+TraceClassUnLoading 參數需要 FastDebug 版的虛擬機支持。

   在大量使用反射、動態代理、CGLib 等字節碼框架、動態生成 JSP 以及 OSGi 這類頻繁自定義 ClassLoader 的場景都需要虛擬機具備卸載的功能,以保證永久代不會溢出。

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