堆空间
- 自由分区(Free Heap Region,FHR)
- 新生代分区(Young Hreap Region,YHR)
- E 是 eden region
- S 是 survivor region
- 老年代分区(Old Heap Region,OHR)
- 如果一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象
- 大对象分区(Humongous Heap Region,HHR)
- 短期存在的巨型对象
- G1 只决定它们是否生存,回收他们占用的空间,从不会移动它们
- 可以跨越多个连续regions。直接分配到老年代,防止反复拷贝移动
- 在global concurrent marking阶段的cleanup 和 full GC阶段回收
- 在分配H-obj之前先检查是否超过 initiating heap occupancy percent和the marking threshold, 如果超过的话,就启动global concurrent marking,为的是提早回收,防止 evacuation failures 和 full GC
- Young-Only 阶段,humongous regions 可能会被回收
- Space-Reclamation,humongous regions 可能会被回收
- 为了减少连续H-objs分配对GC的影响,需要把大对象变为普通的对象,建议增大Region size。
- 一个Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围从1M到32M,且是2的指数
GC
- G1提供了两种GC模式,Young GC和Mixed GC,两种都是完全Stop The World的
- Young GC:选定所有年轻代里的Region。
- Mixed GC:选定所有年轻代里的Region,外加根据global concurrent marking统计得出收集收益高的若干老年代Region。在用户指定的开销目标范围内尽可能选择收益高的老年代Region。Mixed GC不是full GC,它只能回收部分老年代的Region
- 如果mixed GC实在无法跟上程序分配内存的速度,导致老年代填满无法继续进行Mixed GC,就会使用serial old GC(full GC)来收集整个GC heap。G1是不提供full GC的。如果堆是足够大的,Mixed gc 没有回收足够的 old region,或者 concurrent mark 没法及时完成,都可能会导致 full gc。
- 在几次gc后,old gen 的对象占有比超过了 InitiatingHeapOccupancyPercent,gc就会进入并发标记准备(concurrent mark)。Mixed gc 依赖 concurrent mark
- G1默认当分区内存占用阈值达到总内存的45%,会发生Mixed gc(混和GC)
GC 流程
YGC流程:
- 挂起所有用户线程,应用程序暂时不能向外提供响应(STW)
- 计算需要收集的区间(CSet),YGC期间,仅回收Y区,所以所有的Y区就是CSet
- 进行根处理,包含JVM根和Java根。为了扫描全堆,这里只需要把RSet记录的对象加入根即可
- JIT代码扫描等,根据栈中的对象进行递归遍历复制对象
- 因为对象已经有挪动,更新JIT代码中指针存储的对象地址
- 引用处理,一些软、弱、虚、析构(FinalReference)引用等的处理
- 字符串去重等处理
- 清理卡表,也就是把全局卡表中已经处理过的分区的卡表清空
- 进行Redirty操作,也就是重构RSet。释放CSet区
- 尝试回收大对象,如果某个大对象所在的分区没有RSet引用,说明这个大对象已经死亡,可以直接回收
- 尝试扩展内存,参数有GCTimeRatio和GExpandByPercentOfAvailable来决定是否需要扩展,前者在G1中默认值是9,代表的意思是GC占用的时间必须小于1/(1+9),也就是10%,这个值在之前的垃圾收集器默认值是99。如果吞吐量不达标,就会尝试扩展内存,大小由GExpandByPercentOfAvailable决定,默认20,也就是未提交内存的20%
- 如果满足条件的,触发并发标记周期;判断方式是YGC之后,老年代内存占总内存超过一定阈值(参数-XX:InitiatingHeapOccupancyPercent决定,默认45%)触发。
混合回收:
- 根扫描,初始标记:并发标记周期之前一定有一次YGC,所以初始标记会把这次YGC的存活对象也加入根,还有其他根对象,如:栈对象、全局静态对象、JNI对象等等
- 并发标记(concurrent-mark-start~end之间):因为存活对象会比较多,所以这块耗时会比较久,CMS、G1等的这部肯定都是"并发执行的",要不过长的STW时间,会一定程度上的导致服务运行。并且并发标记过程是涉及所有区的,所有老年代区的对象都参与标记。在标记的过程中也会计算每个区的存活对象数目和字节数。
- 再标记:再标记过程是有STW。再标记的目的是处理哪些在并发标记过程中有引用关系变更的对象,见"2.c并发标记的难点"。
- 清理:清理阶段会统计存活对象,并且按照结果对区维度进行排序,以便后续mixed-gc的CSet的选择。同时和会重置RSet,因为老年代的标记已经完成了,这个时候需要删除原来RSet里面保存的引用关系。