《垃圾回收的算法與實現》——引用計數法


基本概念

  • 在對象中引入計數器(無符號整數),用於記錄有多少對象引用了該對象。
  • 通過增減計數器實現對內存的管理。
  • 分配對象時將計數器置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回收白色對象


免責聲明!

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



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