JVM學習-GC之判斷對象存活

在垃圾收集器以前,首先須要判斷哪些對象存活,哪些對象已死(對象死亡的概念表明着,不管經過任何途徑都沒法訪問該對象,沒法被引用鏈可達)算法

引用

  在Java中對於對於引用的概念有四種,分別是強引用、軟引用、弱引用、虛引用。緩存

  • 強引用(Strongly Reference):傳統的引用定義,若是reference類型的數據中存儲的數值表明着另一塊內存的起始地址,就稱該reference數據是表明某塊內存、某個內存的引用
  • 軟引用(Soft Reference):軟引用用來描述一些還有用,但非必須的對象。只被軟引用關聯的對象在系統要發生內存溢出以前,會把這些對象列入回收範圍以內進行二次回收,若是此次回收尚未足夠內存,纔會拋出系統異常。
  • 弱引用(Weak Reference):強度比軟引用更加弱一點,被弱引用關聯的對象只能生存到下一次垃圾收集發生爲止(不管當前內存是否足夠)
  • 虛引用(Phantom Reference):也稱爲「幽靈引用」,這是一種最弱的引用關係,一個對象是否有虛引用存在,不會影響該對象的生命週期,也沒法經過虛引用來獲取該對象的實例。設置虛引用的惟一目的就是爲了這個對象在被垃圾回收時收到一個系統通知

引用計數算法

原理

  引用計數算法基於一個不變式:當且僅持有某一對象的客戶端集合不爲空時,才能判斷對象存活。在引用算法中,對象要與一個引用計數器相連,這個引用計數器通常在對象的頭部槽中,當該對象被引用時,計數器加一,當引用失效時,計數器減一,當該對象的引用計數器爲空或零時,這時即可將對象回收。多線程

優缺點

  優勢:
    1. 引用計數算法的內存管理開銷分攤在程序運行過程當中;
    2. 某一對象成爲垃圾即可以當即回收;
    3. 能夠持續操做即將滿的內存,不須要預留空間;
    4. 當應用程序肯定某一對象並不是共享對象的時候,能夠直接進行破壞性操做,無需建立副本;
    5. 引用計數算法無需運行時系統的支持,特別無需肯定程序的根;
  缺點:
    1. 引用計數給賦值器帶來了額外的時間開銷;
    2. 爲避免多線程競爭可能致使對象釋放過早;
    3. 在簡單的引用計數當中,哪怕最簡單的讀操做也須要引起一次內存寫請求,這裏的內存寫請求會「污染」高速緩存,同時引發額外的內存衝突;
    4. 引用計數算法沒法回收環狀引用結構;
    5. 在某些極端狀況下,對象的引用計數器所佔有的空間開銷很是大;
  在高級的引用計數算法中能夠解決部分問題,可是同時帶來STW(Stop The World),這樣就喪失了引用計數算法的優點線程

可達性分析算法

  從判斷對象是否死亡的角度出發,垃圾收集算法能夠分爲「引用計數式垃圾收集」和「追蹤式垃圾收集」,而「追蹤式垃圾收集」中有標記-清除(Mark-Sweep)、標記-複製(Mark-Copy)、標記-整理(Mark-Compact),這三種垃圾收集算法雖然處理方式各不相同,可是處理的第一階段倒是同樣,追蹤(trace)階段,此階段正是使用的可達性分析算法進行遍歷和標記(Mark)。3d

原理

  從根集合(Root Set)裏面的每個GC Roots的引用關係遍歷GC堆的對象圖,遍歷的路徑稱爲「引用鏈」,若是GC堆裏面某個對象到Root Set沒有引用鏈,就稱爲該對象不可達,對象已經死亡。相似以下圖:cdn

GC Roots

在Java體系裏面,固定能夠做爲GC Roots的對象有以下幾種:對象

  • 在虛擬機棧(棧幀中的本地變量表)中引用的對象,譬如各個線程被調用的方法棧中使用到的參數、局部變量、臨時變量等。
  • 在方法區中類靜態變量引用的對象
  • 在方法區中常量引用的對象,譬如字符串常量池裏的引用
  • 在本地方法棧中JNI(Native方法)引用的對象
  • Java虛擬機內部的引用,如基本數據類型對應的class對象
  • 全部被同步鎖(Syncharonized)持有的對象
  • 反映Java虛擬機內部使用狀況的JMXBean、JVMTI中註冊的回調、本地代碼緩存等。

優缺點

  優勢:
    1. 能夠解決環狀引用;
    2. 佔用對象空間少,標記過程只須要在對象頭設置標記位;
  缺點:
    1. STW(Stop The World),這是一個很嚴重的問題,目前來講,全部的追蹤過程必需要STW;blog

JVM的執行過程

在JVM中判斷一個對象是否存活,依舊要經歷下面幾個過程:生命週期

  1. 第一次標記:
      當該對象進行可達性分析後,發現該對象不可達,將會對該對象進行第一次標記。
  2. 條件篩選:
      在進行完第一次標記後,會對對象進行篩選,篩選條件是有沒有必要執行finalize()方法,若是該對象沒有覆蓋finalize()方法,或者該對象的finalize()方法被執行過(任何一個對象的finalize()方法都只會被系統自動調用一次),則認爲該對象能夠直接回收。
  3. 加入隊列:
      在條件篩選完成以後,被認爲不能直接回收的對象將會被加載到一個F-Queue的隊列中。
  4. 執行方法:
      在加入F-Queue以後,會有一條虛擬機本身創建的、低調度優先級的Finalizer線程去執行(觸發)這些對象的finalize()方法,在finalize()方法中是這些對象存活的最後機會,只要和任意一條引用鏈鏈接上,都不會被回收。
  5. 第二次標記:
      在執行完finalize()方法以後,收集器將會對F-Queue中沒有與引用鏈關聯上的對象進行二次標記。
  6. 對象回收:
      對二次標記的對象或者在條件篩選斷定爲能夠直接回收的對象進行回收。

本文參考《深刻理解Java虛擬機》和《The Garbage Collection Handbook》這兩本書隊列

相關文章
相關標籤/搜索