低延迟垃圾回收器 shenandoah
最近读了《深入理解Java虚拟机 - Jvm高级特性与最佳实践(第三版)》,自己做点笔记,推荐大家有兴趣的可以阅读原书
1. 衡量垃圾回收器的指标
三项最重要的指标分别是:内存占用(Footprint)、吞吐量(Throughput)和延迟(Latency),三者共同构成了一个”不可能三角“。
随着计算机硬件发展,内存占用是我们越来越能忍受的,硬件性能增长,会使吞吐量变高。但对延迟却不是这样,内存的扩大,对延迟反而会带来负面的影响。由此,我们就不难理解为何延迟成为垃圾收集器最被重视的指标了。
2. shenandoah(G1的继承者)
shenandoah与G1的异同点主要是有以下几点:
- 同样是基于Region的堆内存布局 ,同样有用于存放大对象的region,默认回收策略也是优先处理回收价值大的Region;
- 明显的不同则是,支持并发的整理算法,其次是默认不分代收集的,即不会有专门的新生代Region和老年代Region(并不是因为分代对其没有价值,而是基于工作量上的考虑将其实现靠后),最后采用连接矩阵替换了G1中的记忆集,连接矩阵可以简单理解为一张二维表格,当RegionN中有对象指向Region M,就在表格的N行M列中打赏标记。
3.shenandoah的工作过程
阶 段 | 具体过程 |
---|---|
初始标记(停顿) | 标记GC Roots直接关联的对象。停顿时间只与GC Roots的数量相关 |
并发标记 | 遍历对象图,标记全部可达的对象,与用户线程同时运行 |
最终标记(停顿) | 处理并发标记中造成的**SATB(原始快照)**扫描,并且统计出回收价值最高的Region,构成一组回收集。 |
并发清除 | 清理掉整个Region中一个存活对象都没有的Region |
并发回收 | 并发回收是与其他收集器的核心差异,在此阶段需要将Region中还存活的对象复制到其他未被使用的region,但此时用户线程可能还会访问,移动对象是一次性的行为,但移动之后整个内存所有指向该对象的引用都还是旧对象的地址,更新对象的引用却需要时间,shenandoah采用读屏障和Brooks Pointers 的转发指针来解决。 |
初始引用更新(停顿) | 确保并发回收阶段所有的收集器线程已经完成了对象的复制移动 |
并发引用更新 | 据对象的内存物理地址,线性的搜索出引用类型,更新为新值 |
最终引用更新(停顿) | 更新GC Roots中的引用关系 |
并发清理 | 回收建立的回收集中的所有region |
Brooks Pointer:
通常情况下可以采用内存陷阱的方式(通过捕获异常)来更新我们对象的引用,但是这样会导致频繁的用户态到核心态的转化。Brooks point 通过在对象布局的最前面加入一个指针,当对象未移动时,该指针指向对象自己。当对象拥有了一个新的副本之后,该指针就会指向新的副本。