一個對象什么時候才能被回收?


目錄:

1、怎樣判斷一個對象“已死”?
2、引用的分類
3、回收方法區的數據

1、怎樣判斷一個對象“已死”?

在堆里面存放着 Java 世界中幾乎所有的對象實例,垃圾收集器在對堆進行回收前,第一件事情就是要確定這些對象之中哪些還“存活”着,哪些已經“死去”(即不可能再被任何途徑使用的對象)。

那么怎么判斷一個對象“已死”呢,目前有兩種算法可以判斷對象“已死”。

  1. 引用計數算法:
    這個算法的判斷依據是通過給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加 1;當引用失效時,計數器值就減 1;任何時刻計數器為 0 的對象就不可能再被使用的。
    客觀的說,引用計數算法的實現簡單,判斷效率也很高,在大部分情況下它都是一個不錯的算法,也有一些比較著名的應用案例,例如微軟公司的 COM(Component Object Model)技術。但是,至少主流的 Java 虛擬機里面沒有選用引用計算法來管理內存,其中最主要的原因是它很難解決對象之間相互循環引用的問題。

    舉個簡單的例子,請看下面代碼中的 testGC() 方法:對象 objA 和 objB 都有字段 mInstance,賦值令 objA.mInstance = objB 及 objB.mInstance = objA,除此之外,這兩個對象再無任何實際上這兩個對象已經不可能再被訪問,但是它們因為互相引用着對方,導致它們的引用計數都不為 0,於是引用技術算法無法通知 GC 收集器回收它們。
    對象不存在時引用計數器不為 0 的情況
  2. 可達性分析算法:

    在主流的商用程序語言(如Java)的主流實現中,都是稱通過可達性分析來判斷對象是否存活的。這個算法的基本思路就是通過一系列的稱為“GC Roots”的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈,當一個對象到 GC Roots 沒有任何引用鏈相連時,則證明此對象是不可用的。
    可達性分析算法判定對象是否可回收

    在 Java 語言中,可作為 GC Roots 的對象包括下面幾種:
  • 虛擬機棧(棧幀中的本地變量表)中引用的對象。
  • 方法區中類靜態屬性引用的對象。
  • 方法區中常量引用的對象。
  • 本地方法棧中 JNI(即一般說的 Native 方法)引用的對象。

2、引用的分類

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

在 JDK 1.2 之后,Java 對引用的概念進行了擴充,將引用分為強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference) 4 種,這 4 種引用強度一次逐漸減弱。

  • 強引用就是指在程序代碼之中普遍存在的,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。
  • 軟引用是用來描述一些還有用但並非必須的對象。對於軟引用關聯着的對象,在系統將要發生內存溢出異常之前(即內存緊張), 將會把這些對象列進垃圾回收范圍之中進行第二次回收。如果這次回收還沒有足夠的內存,才會拋出內存溢出異常。在 JDK1.2 之后, 提供了 SoftReference 類累實現軟引用。
  • 弱引用是用來描述非必需的對象的,但是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生之前。 當垃圾收集器工作時,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。使用 WeekReference 類來實現弱引用。
  • 虛引用也成為幽靈引用或者幻影引用,它是最弱的一種引用。一個對象是否有虛引用的存在,完全不會對其生存周期時間構成影響, 也無法通過虛引用來取得一個對象實例。唯一的作用就是能在這個對象被收集器回收時收到一個系統通知。使用 PhantomReference 表示。

3、回收方法區數據

方法區的垃圾收集主要回收兩部分內容:廢棄常量和無用的類。回收廢棄常量與回收 Java 堆中的對象非常類似。以常量池中字面量的回收為例,假如一個字符串“abc”已經進入了常量池,但是當前系統沒有任何一個 String 對象是叫做 “abc”的,換句話說,就是沒有任何一個 String 對象引用常量池中的“abc”常量,也沒有其他地方引用了這個字面量,如果這時發生內存回收,而且必要的話,這個“abc”常量就會被系統清理出常量池。常量池中的其他類(接口)、方法、字段的符號引用也與此類似。

判定一個常量為“廢棄常量”比較簡單,而要判斷一個常量池中的類是否是“無用的類”條件則苛刻很多。類需要同時滿足下面 3 個條件才能稱為“無用的類”:

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

最后做個總結:

  1. 我們可以通過 引用計數器 和 可達性算法 來判斷一個對象是否“已死”。引用計數器很難解決對象之間互相循環引用的問題,所以在主流的商用程序語言(如Java)的主流實現中,都是稱通過可達性分析來判斷對象是否存活的。
  2. 對象的引用可以分為 強引用、軟引用、弱引用 以及 虛引用 4 種,其中被 強引用 引用的對象垃圾收集器永遠不會回收掉;被 軟引用 引用的對象,只有當系統將要發生內存溢出時,才會去回收軟引用引用的對象;只被 弱引用關聯的對象,只要發生垃圾收集事件,只被弱引用關聯的對象就會被回收;被虛引用關聯的對象的唯一作用是能在這個對象被回收器回收時受到一個系統通知。
  3. 回收方法區的數據,垃圾收集器主要回收 廢棄常量和無用的類 兩部分內容。


作者:panning
鏈接:https://www.jianshu.com/p/faa30c8e52e8
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM