判断对象是否存活的算法中,引用计数算法广为人知。但是它有个缺点,就是很难解决对象间的相互循环引用。我曾一度误以为jvm也是采用这个算法,只是不知道,jvm是怎么回收循环引用对象的,以为在引用计数上又叠加了其他算法。然而事实是,java用的是可达性分析算法。被自己蠢哭!
写一段有循环引用的代码。(参考《深入理解JAVA虚拟机》--周志明)
$ cat ReferenceCountingGC.java
public class ReferenceCountingGC{
public Object instance = null;
private static final int _1MB = 1024*1024;
private byte[] bigSize = new byte[2*_1MB];
public static void main(String[] args){
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
System.gc();
}
}
编译,JDK环境1.8 虚拟机HotSpot 64位:
$ java -version
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
$ javac ReferenceCountingGC.java
运行并查看GC日志:
$ java -XX:+PrintGCDetails ReferenceCountingGC
[Full GC (System.gc()) [Tenured: 0K->255K(344064K), 0.0021897 secs] 7557K->255K(498944K), [Metaspace: 2478K->2478K(1056768K)], 0.0026702 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
def new generation total 154944K, used 1378K [0x00000005c9400000, 0x00000005d3c10000, 0x0000000670d50000)
eden space 137792K, 1% used [0x00000005c9400000, 0x00000005c9558920, 0x00000005d1a90000)
from space 17152K, 0% used [0x00000005d1a90000, 0x00000005d1a90000, 0x00000005d2b50000)
to space 17152K, 0% used [0x00000005d2b50000, 0x00000005d2b50000, 0x00000005d3c10000)
tenured generation total 344064K, used 255K [0x0000000670d50000, 0x0000000685d50000, 0x00000007c0000000)
the space 344064K, 0% used [0x0000000670d50000, 0x0000000670d8ffd0, 0x0000000670d90000, 0x0000000685d50000)
Metaspace used 2485K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 267K, capacity 386K, committed 512K, reserved 1048576K
从gc日志可以看到内存的回收:
[Full GC (System.gc()) [Tenured: 0K->255K(344064K), 0.0021897 secs] 7557K->255K(498944K), [Metaspace: 2478K->2478K(1056768K)], 0.0026702 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]