堆中几乎存放着全部的对象实例,垃圾收集器在对堆回收以前,第一件事情就是要肯定这些对象哪些还“存活”,那些对象已经“死去”(即不可能被任何途径使用的对象)算法
给对象中添加一个引用计数器,每当又一个地方引用它时,计数器值加1;当引用失效时,计数器减1;任什么时候刻计数器都为0的对象就是不可能在被使用的.可是Java语言中没有选用引用计数法来管理内存,其中最主要的缘由是它很难解决对象之间相互循环依赖的问题.bash
例如: 在testGC中,对象objA和对象objB都有字段instance,赋值objA.instance=objB.instance及objB.instance=objA.instance,除此以外这两个对象再无其余任何引用,实际上这两个对象都已经不能再被访问,可是他们由于他们互相引用着对方,他们的引用计数都不为0,因而引用计数算法没法通知GC收集器回收它们. 打印GC详细信息: -XX:+PrintGCDetailsspa
public class ReferenceCountingGC {
public Object instance = null;
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日志为:3d
[GC (System.gc()) [PSYoungGen: 334480K->4736K(334848K)] 597914K->270331K(1017536K), 0.0019879 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[Full GC (System.gc()) [PSYoungGen: 2408K->0K(298688K)] [ParOldGen: 0K->3363K(682688K)] 3408K->3363K(981376K), [Metaspace: 3162K->3162K(1056768K)], 0.0050515 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 153088K, used 3947K [0x0000000715580000, 0x0000000720000000, 0x00000007c0000000)
eden space 131584K, 3% used [0x0000000715580000,0x000000071595afc0,0x000000071d600000)
from space 21504K, 0% used [0x000000071d600000,0x000000071d600000,0x000000071eb00000)
to space 21504K, 0% used [0x000000071eb00000,0x000000071eb00000,0x0000000720000000)
ParOldGen total 349696K, used 872K [0x00000005c0000000, 0x00000005d5580000, 0x0000000715580000)
object space 349696K, 0% used [0x00000005c0000000,0x00000005c00da3b8,0x00000005d5580000)
Metaspace used 3169K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 346K, capacity 388K, committed 512K, reserved 1048576K
复制代码
对照上面的图,GC日志中的PSYoungGen(PS是指Parallel Scavenge)为Eden+FromSpace,而整个YoungGeneration为Eden+FromSpace+ToSpace。日志
咱们设置的新生代大小为10240K,这包括9216K大小的PSYoungGen和1024K大小的ToSpace。其中,PSYoungGen中的Eden:FromSpace为8:1,code
这包括8192K的Eden和1024K的FromSpace。cdn
参数设置为:对象
-XX:+PrintGCDetails -XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=8 -XX:NewSize=10M -XX:MaxNewSize=10Mblog
参数解释:内存
-XX:+PrintGCDetails 启用日志
-XX:-UseAdaptiveSizePolicy 禁用动态调整,使SurvivorRatio能够起做用
-XX:SurvivorRatio=8 设置Eden:Survivior=8
-XX:NewSize=10M -XX:MaxNewSize=10M 设置整个新生代的大小为10M
默认垃圾收集器为:Parallel Scavenge
在运行日志结果中咱们能够看到GC日志包含“2408K->0k”,老年代从2408K(约2M,其实就是objA与objB)变为了512K,意味着虚拟机并无由于这两个对象互相引用就不回收它们,这也证实虚拟机并非经过引用计数法判断对象是否存活的.能够看到对象进入了老年代,可是你们知道,对象刚建立的时候是分配到新生代中到,要进入老年代须要到new ObjA才行,可是这里objA和objB却进入了老年代.这是由于Java堆区会动态增加,刚开始时堆区较小,对象进入老年代还有一些规则,当survior空间中同一代的对象大小之和超过survior空间的一半时,对象将直接进入老年代.
这个算法的基本思路是经过一系列名为“GC Root”的对象做为起点,从这些节点开始往下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证实此对象是不可用到,下图对象object5,object6,object7虽然有互相判断,但它们到GC Roots是不可达到,因此它们将会断定为可回收对象
在Java语言里,可做为GC Roots对象的包括以下几种:
即便在可达分析算法中不可达的对象,也并不是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程. 标记的前提是对象在进行可达性分析后发现没有与GC Roots互相连的引用链.