君子以自强不息。
简介
新生代回收(Young GC,YGC)。YGC 是针对部分内存进行的垃圾回收,通常 YGC 花费的时间都比较小。YGC 收集的内存是不固定的,每次回收的内存可能并不相同,即每次回收的分区数目是不固定的,但是每一次 YGC 都是收集所有的新生代分区,所以每一次 GC 之后都会调整新生代分区的数目。调整的数目依赖于停顿预测模型。
算法
YGC 算法主要分为两个部分:并行部分和其他部分。
并行部分:
- 根扫描并处理;处理过程会把根直接引用的对象复制到新的 Survivor 区,然后把被引用对象的 field 入栈等待后续的复制处理。
- 处理老生代分区到新生代分区的引用;首先会更新所有的代际引用,即更新 RSet,然后从 RSet 出发,把 RSet 所在卡表对应的分区内存块中所有的对象都认为是根,把这些根引用的对象复制到新的 Survivor 区,然后把被引用对象的 field 入栈等待后续的复制处理。
- JIT 代码扫描。
- 根据栈中的对象,进行深度递归遍历复制对象。
其他部分:
- JIT代码位置更新,在并行任务中已经对代码进行了扫描和复制,这里会更新相关指针所指向的位置。
- 引用处理,即把引用中使用的存活对象也要复制到新的分区。
- 字符串去重优化回收。
- 清除卡表,就是把全局卡表中已经处理过的分区对应的卡表清空。
- JIT代码回收,代码已经可以回收,实际上是删除相关的引用,这一部分代码对GC影响不大。
- 如果Evac失败,则进行处理,主要的工作就是恢复对象头。
- 引用再处理,把引用中还活着的对象放入引用队列中,这个和引用特殊的设计有关。
- 进行Redirty,主要工作就是重构RSet,包括收集过程中,因为对象移动需要重构老生代分区到新生代分区新分区的引用,这个过程不仅仅是收集成功也包括回收失败,不过收集失败需要做额外的记录;Redirty通常是并行执行的。
- 释放CSet,在这个位置可以启动释放内存,即把这些分区放入自由列表(FreeList),供后续使用,这里的后续指的是对象分配时如果需要新的分区,可以直接从自由列表获取。当然分区可能作为新生代分区也可能作为老生代分区。
- 尝试大对象回收,处理比较简单,只要判定这些大对象所在的分区是否有RSet引用,且只需要判断大对象所在第一个分区,如果没有引用则说明整个大对象肯定已经死亡,有引用则说明大对象可能还活着,在并发标记中进一步处理。
- 尝试扩展内存,这里的扩展就是用到我们在前面讲到的根据GCTimeRatio和G1ExpandByPercentOfAvailable来判断是否可以扩展,如果可以,扩展多大的内存。
- 调整新生代分区的数目,调整Refinement Zone阈值等;主要是根据GC的执行时间和目标停顿时间预测下次可能发生垃圾回收时能接受的最大分区数。
- 如果可能的话,启动并发标记。
收集过程
1、进行收集之前需要 STW。
2、选择要收集的 CSet,对于 YGC 来说整个新生代分区就是 CSet。
3、进入并行任务处理。
4、其他任务处理,大部分都是串行执行。