引用計數算法(Reference Counting)
垃圾回收的困難不在于 實際回收垃圾的過程,而是在于在哪些地方找到垃圾。當一個對象不在被引用時候 則這個對象被認為是可以被回收的。
算法描述:
在每個對象中有個一個字段refCount 記錄該對象被引用的次數,refCount是在java program 中不能被通路的,隻是可以被jvm 修改或者更新。
例如:
Object p = new Integer (57);
當建立一個Integer 類的執行個體,隻有一個變量p 指向這個對象,因為這個引用計數refCount=1
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiYWan5COmJTO4gzM3cTYiZWL3MTM40iZ2gzMtUWOilTLzkjN1QzM0UzLcBjN3AzLcdjNwAzLcRnbl1GajFGd0F2LcRWYvxGc19CXt92YuUWelRXauwGZvw1LcpDc0RHaiojIsJye.gif)
接着考慮下面的語句:
Object p = new Integer (57);
Object q = p;
這個語句隻是建立了一個integer 執行個體,p和q 都指向同一個對象,因為 這個執行個體的
refCount =2
總的來說,每次一個引用變量指派給另外一個變量時候,總是有必要更新幾個引用計數。假定p和q 都是引用變量,下面的指派
p = q;
在jvm 中将會實作成如下僞代碼:
if (p != q)
{
if (p != null)
--p.refCount;
p = q;
if (p != null)
++p.refCount;
}
假如p,q初始化如下:
Object p = new Integer (57);
Object q = new Integer (99);
如圖a所描述的,構造了2個integer 執行個體,每個執行個體的refCount=1/
接下來,我們把p=q。
如圖b所描述的,在指派之後,p和q都指向了同一個對象,這個對象的refCount=2,而對Integer(57)這個對象的引用已經變成了0,這隻是gc去回收這個對象。
引用計數的開銷在兩個方面:
A:每個對象需要一個特殊的refCount 字段,在每個對象中 需要為這個字段配置設定額外的存儲空間
B:每次一個引用被指派給另外一個引用,這個refCount 就必須用上面的代碼進行調整,這明顯增加了指派語句的執行時間。
引用的計數算法的優點:
垃圾回收器很容易定位出需要回收的對象,并且并不需要等到沒有足夠的空間才啟動垃圾回收,當一個對象的refCount=0時,我們可以立即啟動垃圾回收。
例如 p=q
if (p != q)
{
if (p != null)
if (--p.refCount == 0)
heap.release (p);
p = q;
if (p != null)
++p.refCount;
}
用上述方法,垃圾将會被增量回收。
B:指向複雜對象:
例如:
Object p = new Association (new Integer (57), new Integer (99));
圖a描述了Object p = new Association (new Integer (57), new Integer (99));
這個語句執行完之後的記憶體内從。
Association 執行個體的引用數量refCount=1,因為之後一個變量p指向它。
相似的兩個integer執行個體的refCount =1,因為之後一個Association 執行個體指向它們。
圖:當一個對象指向另一個對象的引用計數
Suppose we assign the value null to the variable p.
假定我們将p=null,如圖b所描述,association執行個體的refCount=0,這個association執行個體就變得可以被回收了。
Association 這個執行個體會一直存在直到gc開始回收Association 。圖d
描述了當Association 被gc時,,gc調整Association 執行個體引用的refCount 。經過調整之後 ,這兩個Intege 對象的refCount=0也可以被gc.
C:Reference Counting 算法失效:
如圖a所示循環的單連結清單,head 變量指向單連結清單的頭,并且這個單連結清單的最後一個元素也指向單連結清單的頭。 因為對于第一個元素的refCount=2,在連結清單中的其他元素的refCount=1
Figure: Why reference counting fails.
接下來,我們把head變量的值設定為null,head=null. 如圖b所表示,連結清單的第一個元素的refCount減少了因為head變量不在指向它,但是它的refCount !=0,因為連結清單的最後一個元素指向連結清單的第一個元素。
現在我們遇到一個問題:在連結清單中的所有元素的refCount !=0,因而他們不會給gc.但是對于LinkedList 對象沒有外部引用,LinkedList對象裡的元素實際上是要被回收的。
總的來說,引用計數算法在碰到循環引用時候将不會奏效。但是JAVA 對于循環結構的建構是不會預防的。是以,引用計數算法對于複雜對象來說本身不是一個适合的垃圾回收模式。但是對于那些不指向其他對象的簡單對象,這個是一個非常有用技巧。