垃圾回收器算法之引用計數器法
微軟將運行在公共語言運行時裏的代碼成爲託管代碼;但是從更廣泛的意義上來說,只要語言提供了自動內存管理功能,我們使用其開發的代碼都可以稱爲託管代碼;自動內存管理即我們平時所說的垃圾回收器,垃圾回收器的實現是一個複雜的過程,其中涉及到很多的細節;垃圾回收器的難點並不是垃圾的回收過程,而是定位垃圾對象。當一個對象不再被引用的時候就可以被回收了,但是我們怎樣才能知道其沒有被引用呢?
算法定義
爲每個對象增加一個字段記錄被引用的次數,並由運行時跟蹤和更新引用的總數;
object p = new ComparableInt32(57);
object q = p;
我們實例化了一個對象ComparableInt32,並將其賦值給變量p,此時p引用了該對象,所以其計數器爲1;然後我們又用p給變量q賦值,此時q也引用了該變量,所以其計數器變爲2;如下圖所示
從上圖我們可以看到,引用類型每次賦值都需要運行時更新計數器,運行時的更新代碼可能如下
if (p != q)
{
if (p != null)
--p.refCount;
p = q;
if (p != null)
++p.refCount;
}
計數器算法的一大優勢就是不用等待內存不夠用的時候,才進行垃圾的回收,其可以在賦值操作的同時,檢查計數器是否爲0,如果是的話就可以立即回收;運行時的代碼可能如下
if (p != q)
{
if (p != null)
if (--p.refCount == 0)
heap.Release(p);
p = q;
if (p != null)
++p.refCount;
}
計數器算法的一大缺點就是不能解決循環引用的問題;如下圖,我們構造了一個列表,我們將最後一個元素的next屬性指向第一個元素,即引用第一個元素,從而構成循環引用;這個時候如果我們將列表的頭head賦值爲null,此時列表的各個元素的計數器都不爲0,同時我們也失去了對列表的引用控制,從而導致列表元素不能被回收!
算法特點
1. 需要單獨的字段存儲計數器,增加了存儲空間的開銷;
2. 每次賦值都需要更新計數器,增加了時間開銷;
3. 垃圾對象便於辨識,只要計數器爲0,就可作爲垃圾回收;
4. 及時回收垃圾,沒有延遲性;
5. 不能解決循環引用的問題;