GC算法-引用計數法

概述

引用計數法又是什麼鬼呢? 顧名思義, 對對象的引用進行計數. 通過記錄每個對象被引用的次數, 來確定這個對象是否可以被回收.

實現

首先, 對對象的引用數量進行管理, 什麼時候會更新呢?

  1. 創建對象: 新建一個對象(對這個新的對象引用數量+1)
  2. 更新指針: 將一個指向A對象的指針重新指向B對象(將A對象引用數量-1, B對象引用數量+1)

這次就不上代碼了, 簡單介紹一下思路就行. (我哥說代碼看着費勁)

前提: 我們有一個全局的空閒地址鏈表: FREE_HEAD

創建對象的操作

  1. 從FREE_HEAD中尋找內存
  2. 若找到了, 該對象計數器置爲1, 返回
  3. 若沒有找到, 內存擴容, 返回1

更新指針的操作

  1. 將新的對象引用計數+1
  2. 將舊的對象引用計數-1. 若-1後引用數量爲0, 則將該對象及所有的子對象添加到FREE_HEAD鏈表中.

實現說起來簡簡單單, 畢竟我也不用真的去實現, 簡單想一下.

分析

在上一次的標記清除算法中, GC在每次內存不足時運行, 勢必會導致程序暫停時間比較長. 但引用計數則在每次指針變更的同時進行管理, 在產生新的垃圾的時候立刻進行回收. 這就體現出它的幾個優勢了:

  1. 最大暫停時間短.
  2. 產生垃圾可立即回收

當然, 只說優勢不說劣勢都是扯犢子. 首先, 引用計數的優勢也會成爲它的劣勢, 計數頻繁的計算, 會拖累程序的速度. 而且每個對象都要開拓空間來保存引用數量. 當然了, 還有經常被說到的循環引用的問題. 等等吧.

  1. 頻繁的更新引用計數拖累程序速度
  2. 每個對象需要開拓額外空間保存引用計數
  3. 循環引用對象無法被回收(就是A引用B, B引用A. 但是他們都沒有被其他對象引用, 導致他倆的引用始終爲1, 無法回收)

當然, 針對問題, 偉大的前人總是有辦法去解決. 比如:

延遲計數法: 針對頻繁更新計數器的問題而提出的. 大概意思就是不去實時的對引用數量進行更新, 將引用數量爲0的記錄到一個待處理的鏈表中, 當需要新的內存時再統一處理. 但是這樣又會增大暫停時間, 纔不要.

Sticky引用計數法: 引用計數通過額外的空間保存引用數量, 但這個必然會有最大值, 比如用1個字節, 則引用數量超過256的就記不下了. 這個方法對超出範圍的處理方式很簡單, 什麼都不做, 不去回收, 畢竟被引用這麼多次, 該對象定然很重要. 那這些對象不就永遠都不能被回收了麼? 可以, 等到沒有內存了, 使用標記清除算法將所有對象過一遍.

當然, 針對引用計數法還有很多演變, 有些還是很有意思的, 有些是我看不懂的.


垃圾回收的整體思路分兩個流派(我所知道的):

  1. 引用計數: 就是上面說的這種
  2. 可達性: 就是標記清除那種, 判斷一個對象是否可以到達.

引用計數的最大優勢應該就是不需要暫停程序去進行回收了, 隨使用隨回收. 但劣勢也很明顯: 需要計數器額外空間以及循環引用的問題.

個人是比較喜歡引用計數的, 實時性又高, 又不需要太多的額外空間. 只是需要在編寫代碼的時候刻意規避循環引用, 或者其他方法規避一下? 甚至不去處理都刻意, 如果只有少數的話(如果有很多, 還是換個算法吧).

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章