引用计数算法(Reference Counting)
垃圾回收的困难不在于 实际回收垃圾的过程,而是在于在哪些地方找到垃圾。当一个对象不在被引用时候 则这个对象被认为是可以被回收的。
算法描述:
在每个对象中有个一个字段refCount 记录该对象被引用的次数,refCount是在java program 中不能被访问的,只是可以被jvm 修改或者更新。
例如:
Object p = new Integer (57);
当创建一个Integer 类的实例,只有一个变量p 指向这个对象,因为这个引用计数refCount=1
接着考虑下面的语句:
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 对于循环结构的构建是不会预防的。因此,引用计数算法对于复杂对象来说本身不是一个适合的垃圾回收模式。但是对于那些不指向其他对象的简单对象,这个是一个非常有用技巧。