基本概念
- 在對象中引入計數器(無符号整數),用于記錄有多少對象引用了該對象。
- 通過增減計數器實作對記憶體的管理。
- 配置設定對象時将計數器置1。
- 更新引用時先對新指定的對象進行計數器加,而後才對舊對象進行減。
- 在對計數器做減法時,判斷其計數器是否等于0,等于0 表示為垃圾,即可進行回收。
- 在更新引用時就進行了垃圾的标記與回收,是以STW會很短而且當對象變垃圾時能立馬被回收。
優缺點
優點
- 即刻回收垃圾,在更改引用時就知道該對象是否為垃圾若是垃圾立馬進行回收(但是該操作會占用使用者線程的時間片)
- STW短,回收垃圾不需要周遊堆了。
- 不需要根據GC root周遊。
缺點
- 計數器值增減頻繁。
- 計數器需要占用很多位。
- 實作繁瑣,更新引用時很容易導緻記憶體洩露。
- 循環引用無法回收(最重要的缺點)
改進
延遲引用計數法
針對計數器增減頻繁
- 從根引用的指針變化不修改計數器,為了使還存在引用的對象被回收,引入ZCT(Zero Count Table)記錄計數器為0的對象(當為0時不直接删除而是加入到該表中)。
- 配置設定塊時,先正常配置設定(應該也是通過空閑連結清單),如果配置設定失敗則從ZCT中找可以真正的垃圾對象進行回收與配置設定。
- 掃描ZTC表需要周遊GC root,對所有GC root引用的計數器加1,而後周遊ZTC,此時計數器為0的即為垃圾,最後對GC root引用對象減一(還原)。
Sticky引用計數法
針對計數器占用很多位
由于大多數對象的引用并不會很多,可以減少計數器的位寬,當計數器溢出時有兩種處理方式:
- 不管,對于溢出的對象不再增減計數器。此時無法判斷是否為垃圾是以不再關此對象,溢出的可能性很低是以是一個可以考慮的解決辦法。
- 使用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回收白色對象