算法:给对象中添加一个引用计数器,每当有一个地方引用它的时候,计数器就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象是不可能被使用的。
但是JVM并没有使用引用计数来管理内存,其中最重要的原因就是它很难解决对象之间的相互引用关系。
测试JVM不使用引用计数算法的代码如下(注意参数):
/**
* VM args : -XX:+PrintGCDetails
* testGC()方法执行后,objA和objB会不会被GC呢?
*/
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,objA和objB是否被收回?
System.gc();
}
}
得到最终结果为:
[GC (System.gc()) [PSYoungGen: 5407K->704K(18944K)] 5407K->712K(62976K), 0.0010308 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 704K->0K(18944K)] [ParOldGen: 8K->530K(44032K)] 712K->530K(62976K), [Metaspace: 2568K->2568K(1056768K)], 0.0040450 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 18944K, used 164K [0x00000000eaf00000, 0x00000000ec400000, 0x0000000100000000)
eden space 16384K, 1% used [0x00000000eaf00000,0x00000000eaf290d0,0x00000000ebf00000)
from space 2560K, 0% used [0x00000000ebf00000,0x00000000ebf00000,0x00000000ec180000)
to space 2560K, 0% used [0x00000000ec180000,0x00000000ec180000,0x00000000ec400000)
ParOldGen total 44032K, used 530K [0x00000000c0c00000, 0x00000000c3700000, 0x00000000eaf00000)
object space 44032K, 1% used [0x00000000c0c00000,0x00000000c0c84a88,0x00000000c3700000)
Metaspace used 2574K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 286K, capacity 386K, committed 512K, reserved 1048576K
我们可以看到PSYoungGen: 5407K->704K(18944K),意味着虚拟机并没有因为两个对象相互引用就不回收他们,这也从侧面说明虚拟机并不是通过引用计算器来清理内存