CMS收集器无法处理浮动垃圾,由于CMS并发清理阶段用户线程还在运行着,伴随着程序运行自然就会有新的垃圾不断产生,这部分垃圾出现的标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC中再清理。这些垃圾就是“浮动垃圾”。同时为了保证在垃圾回收的同时用户线程也可以正常工作,所以不可能对整个区域进行回收,需要预留一部分区域给用户线程,如果在垃圾回收阶段,预留的垃圾回收区域不足,就可能会出现“Concurrent Mode Failure(并发模式故障)”失败而导致Full GC产生。
CMS是一款“标记--清除”算法实现的收集器,容易出现大量空间碎片。当空间碎片过多,将会给大对象分配带来很大的麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。
G1收集器G1是一款面向服务端应用的垃圾收集器。G1具备如下特点:
并行与并发:G1收集器能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短STW停顿时间。部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。
分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。它能够采用不同的方式去处理新创建的对象和已经存活了一段时间,熬过多次GC的旧对象以获取更好的收集效果。
空间整合:与CMS的“标记--清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器。从局部上来看是基于“复制”算法实现的。
可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内。
在G1中Heap被分成一块块大小相等的region,Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围从1M到32M,且是2的指数。如果不指定,那么G1会根据Heap大小自动决定。保留新生代和老年代的概念,但它们不需要物理上的隔离。每块region都会被打唯一的分代标志(eden,survivor,old),代表一个分代类型的region可以是不连续的。eden regions构成Eden空间,survivor regions构成Survivor空间,old regions构成了old 空间。通过命令行参数-XX:NewRatio=n来配置新生代与老年代的比例,n为整数,默认为2,即比例为2:1;-XX:SurvivorRatio=n可以配置Eden与Survivor的比例,默认为8。
G1收集器进行回收大致可分为以下几个阶段:
初始标记:同CMS功能基本一致初始标记是用于标记直接与GC Roots关联的对象,不需要遍历下去,所需的时间很短。这一过程会发生STW(Stop the World)。
并发标记:并发标记就是遍历所有与GC Roots直接或者间接关联的对象。遍历整个堆寻找活跃对象,这个发生在应用运行时,这个阶段可以被年轻代垃圾回收打断。
重新标记:这一过程是为了标记在前面标记过程中发生变动的对象,和CMS的重新标记过程功能上基本保持一致。但是G1使用一个叫作snapshot-at-the-beginning(SATB)的比CMS收集器的更快的算法。
筛选回收:进行垃圾回收,G1保留了YGC并加上了一种全新的MIXGC用于收集老年代。G1中没有Full GC,G1中的Full GC是采用serial old的Full GC。
Young GC当Eden空间不足时就会触发YGC。在G1中YGC也是采用复制存活对象到survivor空间,对于对象的存活年龄满足晋升条件时,把对象移到老年代。
在对新生代进行垃圾回收时,需要判断哪些对象能够会被回收。这里判断的方法也是采用可达性分析,标记与GC Roots直接或间接关联的对象。在CMS中使用了Card Table的结构,里面记录了老年代对象到新生代引用。G1也是使用这个思路,定义了一个新的数据结构:Remembered Set。在G1收集器中,Region之间的对象引用以及其他收集器中的新生代与老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描的。G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中(在分代的例子中就是检查是否老年代中的对象引用了新生代中的对象),如果是,便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set之中。在进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。
Full GCfull gc是指对包括新生代、老年代和方法区(元空间)等地区进行垃圾回收。
full gc的触发包括以下几种情况:
老年代空间不足
新生代对象晋升到老年代时,老年代剩余空间低于新生代晋升为老年代的速率,会触发老年代回收
minor gc之后,survior区内存不足,将存活对象放入老年代,老年代也不足,触发Full GC。本质上还是老年代内存不足。
System.gc().
理解GC日志这里介绍一些打印出的gc日志的信息: