什么时候回收内存 - morris131/morris-book GitHub Wiki
在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(即不可能再被任何途径使用的对象)。判断对象是否存活的算法有引用计数算法,可达性分析算法。
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。 这个算法有一个致命的缺陷,那就是对于循环引用的对象无法进行回收。
package com.morris.jvm;
/**
* testGC()方法执行后,objA和objB会不会被GC呢?
* vm args:-XX:+PrintGCDetails
* @author morris
*/
public class ReferenceCountingGC {
public Object instance = null;
private static final int _1MB = 1024 * 1024;
/**
* 这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收过
*/
@SuppressWarnings("unused")
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;
// 假设在这行发生GC,objA和objB是否能被回收?
System.gc();
}
}
运行结果:
[GC (System.gc()) [PSYoungGen: 6092K->632K(38400K)] 6092K->640K(125952K), 0.0006584 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 632K->0K(38400K)] [ParOldGen: 8K->526K(87552K)] 640K->526K(125952K), [Metaspace: 2655K->2655K(1056768K)], 0.0156118 secs] [Times: user=0.00 sys=0.00, real=0.02 secs]
Heap
PSYoungGen total 38400K, used 333K [0x00000000d5900000, 0x00000000d8380000, 0x0000000100000000)
eden space 33280K, 1% used [0x00000000d5900000,0x00000000d59534a8,0x00000000d7980000)
from space 5120K, 0% used [0x00000000d7980000,0x00000000d7980000,0x00000000d7e80000)
to space 5120K, 0% used [0x00000000d7e80000,0x00000000d7e80000,0x00000000d8380000)
ParOldGen total 87552K, used 526K [0x0000000080a00000, 0x0000000085f80000, 0x00000000d5900000)
object space 87552K, 0% used [0x0000000080a00000,0x0000000080a83878,0x0000000085f80000)
Metaspace used 2662K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K
从运行结果中可以清楚看到,GC日志中包含“4603K->210K”,意味着虚拟机并没有因为这两个对象互相引用就不回收它们,这也从侧面说明虚拟机并不是通过引用计数算法来判断对象是否存活的。
通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
在Java语言中,可作为GC Roots的对象包括下面几种: 虚拟机栈(栈帧中的本地变量表)中引用的对象。 方法区中类静态属性引用的对象。 方法区中常量引用的对象。 本地方法栈中JNI(即一般说的Native方法)引用的对象。