在判断对象是否存活时,引用计数算法的做法是:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
引用计数算法(Reference Counting)的实现简单,判定效率也很高,在大部分情况下时一个不错的算法,也有一些比较著名的案例,例如MS的COM(Component Object Model)技术、使用ActionScript3的FlashPlayer、Python语言和在游戏脚本领域被广泛应用的Squirrel中都使用了引用计数算法进行内存管理。但是,主流的Java虚拟机里面没有选用计数算法来管理内存,主要原因就是他很难解决对象之间相互循环引用的问题。例如以下代码:public class ReferenceCountingGC {
public Object instance = null;
private static final int _1MB = 1024 * 1024;
//占用内存,以便在GC日志中看清楚是否被回收过
private byte[] bigSize = new byte[2 * _1MB];
public static void testGC(){
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
//触发gc
System.gc();
}
}
当执行到objA.instance = objB时,此时objA虽然没被引用,但objA对属性instance的引用使得objA的引用计数已经+1。执行完objB.instance = objA后,objA和objB的引用计数均为2.
当objA和objB都赋值为空后,两个对象的引用计数均减1,但是引用计数仍然不为0,所以虽然这两个对象再无任何引用,也就是说这两个对象已经不再可能被访问,若虚拟机采用的是计数算法,还是无法通知GC收集器回收它们。
在运行参数中加入:-XX:+PrintGCDetails。查看运行结果:
虚拟机提供了-XX:+PrintGCDetails这个收集器日志参数,告诉虚拟机在发生垃圾收集
行为时打印内存回收日志,并且在进程退出的时候输出当前的内存各区域分配情况。 在实际
应用中,内存回收日志一般是打印到文件后通过日志工具进行分析,不过本实验的日志并不
多,直接阅读就能看得很清楚。
[GC (System.gc()) [PSYoungGen: 8028K->480K(76288K)] 8028K->488K(251392K), 0.0009484 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] |
从GC日志中可以清楚地看出,“8028K->488K(251392K)”和“480K->0K(76288K)”意味着虚拟机并没有因为两个对象互相引用就不回收它们,这也说明虚拟机并不是通过引用计数算法来判断对象是否存活的。