天天看點

java垃圾回收之 引用計數法

基本概念

  • 在對象中引入計數器(無符号整數),用于記錄有多少對象引用了該對象。
  • 通過增減計數器實作對記憶體的管理。
  • 配置設定對象時将計數器置1。
  • 更新引用時先對新指定的對象進行計數器加,而後才對舊對象進行減。
  • 在對計數器做減法時,判斷其計數器是否等于0,等于0 表示為垃圾,即可進行回收。
  • 在更新引用時就進行了垃圾的标記與回收,是以STW會很短而且當對象變垃圾時能立馬被回收。

優缺點

優點

  1. 即刻回收垃圾,在更改引用時就知道該對象是否為垃圾若是垃圾立馬進行回收(但是該操作會占用使用者線程的時間片)
  2. STW短,回收垃圾不需要周遊堆了。
  3. 不需要根據GC root周遊。

缺點

  1. 計數器值增減頻繁。
  2. 計數器需要占用很多位。
  3. 實作繁瑣,更新引用時很容易導緻記憶體洩露。
  4. 循環引用無法回收(最重要的缺點)

改進

延遲引用計數法

針對計數器增減頻繁

  • 從根引用的指針變化不修改計數器,為了使還存在引用的對象被回收,引入ZCT(Zero Count Table)記錄計數器為0的對象(當為0時不直接删除而是加入到該表中)。
  • 配置設定塊時,先正常配置設定(應該也是通過空閑連結清單),如果配置設定失敗則從ZCT中找可以真正的垃圾對象進行回收與配置設定。
  • 掃描ZTC表需要周遊GC root,對所有GC root引用的計數器加1,而後周遊ZTC,此時計數器為0的即為垃圾,最後對GC root引用對象減一(還原)。

Sticky引用計數法

針對計數器占用很多位

由于大多數對象的引用并不會很多,可以減少計數器的位寬,當計數器溢出時有兩種處理方式:

  1. 不管,對于溢出的對象不再增減計數器。此時無法判斷是否為垃圾是以不再關此對象,溢出的可能性很低是以是一個可以考慮的解決辦法。
  2. 使用GC标記-清除算法,先把所有對象(==我覺得處理已溢出的就可以?==)的計數器置0,而後周遊GC root,可引用的将計數器置1,清除階段則清理那些計數器為0的。

1位引用計數法

Sticky的極端例子

  • 計數器隻有1位,0表示隻有一個引用(UNIQUE)1表示多引用(MULTIPLE)。
  • 更新引用時通過複制某個指針來更新。

==覺得有問題,怎麼找到可以被複制的指針呢?指針是單連結清單,無法通過對象找到其指針吧==

部分标記-清除算法

針對循環引用

  • 為了解決循環引用不能被回收有采用在某些時候加入GC标記-清除算法,然而循環引用往往很少,效率很低。
  • 該方法是隻對可能存在循環引用的對象群進行Sweep。
  • 對象被标記為四種顔色,黑:不是垃圾,白:垃圾,灰:搜尋過的對象,陰影:可能是循環引用對象。上色采用兩位标記
  • 當删除引用關系時,先自減,而後如果計數器為0則回收該對象,否則将該對象置為陰影并加入到hatch_queue
  • 當空閑連結清單無法配置設定時将去hatch_queue中掃描是否存在循環垃圾可進行回收。
  • 周遊取hatch_queue中對象分别做paint_gray、scan_gray和collect_white
  • paint_gray将對象置灰,而後将所有子對象計數器減1,并調用疊代paint_gray.
  • scan_gray将灰色中計數器為0的對象置白,大于0的塗成黑色。
  • collect_white回收白色對象

繼續閱讀