之前我分享了一个动画视频《动画演示 JVM G1 垃圾回收算法的内存布局和年轻代的垃圾回收过程》。本文简要列举一下G1算法的特点。
策略上的改进
- G1 是采用增量的方式回收垃圾。为了控制停顿时间,一次收集一部分。
- G1 的停顿时间更加可预测。它会尽量(90%)地保证停顿时间
G1 的堆heap不再采用老式的分代分区方式,而是引入了一个新的布局方式,2048个大小相等region。region的大小在1M到32M之间。G1 维护了一个空闲的region列表。根据需要可以从列表里分配一个作为年轻代或老年代。G1 的策略是在保证停顿时间<200毫秒的情况下,尽可能回收那些存活对象少,垃圾对象多的region。这样的回收效率会很高。也是Garbage First的名字由来。G1 GC is an incremental parallel compacting GC that provides more predictable pause times compared to CMS GC and Parallel Old GC.
年轻代
G1 的年轻代的GC还是采用原来的标记,复制和清除算法,只不过是发生在region上。G1 根据XX:MaxGCPauseMillis的设置,自动调整年轻代的region的个数。G1 的新对象还是会在eden region分配。同时为大对象(大小超过region一半)设计了一类专门的Humongous region。并发标记
当heap用量大于–XX:InitiatingHeapOccupancyPercent,默认45%的时候,就会触发一次并发标记(a multi-phased concurrent marking cycle)。采用了更加先进的标记算法:Snapshot-At-The-Beginning (SATB)。
- The Initial Mark Phase - 初始标记。这几乎就是一次年轻代的GC。会停顿(stop the world)。
- The Root Region Scanning Phase - 根region的扫描。经过初始标记之后,存活对象都复制到了一个存活 survivor region。这个阶段就是以这些存活对象为根,寻找老年代的存活对象。注意:这个过程不用停顿用户程序(也就是说eden区可以继续分配对象),但是一定不能发生新一轮的年轻代的gc。(否则这个survivor region就被修改了)。
- The Concurrent Marking Phase - 并发标记。gc会全heap寻找存活对象。注意,这个过程不用停顿用户程序,但是可以被年轻代的gc打断。
- The Remark Phase - 再标记。这是为了结束整个标记过程。会停顿(stop the world)。
- The Cleanup Phase - 清算。 这是并发标记的最后一步。计算每个region的存活指数(live-ness)。在计算的时候会停顿(stop the world)。
混合垃圾回收 mixed collection
年轻代的gc只会回收年轻代的region。而mixed collection会同时回收年轻代和老年代的region。
经过并发标记之后,gc就有了mixed collection所需要的必要信息:该不该回收,该回收哪些region。
整个过程和年轻代的gc差不多。也是复制,清除。对于年轻代的,复制到survivor region或者提升到old region。对于年老代的region的对象,复制到年老代的region。
mixed collection不是一次性的,通常会发生好几轮(因为需要控制停顿时间,不能一次收集太多old region)。直到收集到足够多的年老代的region,也就是heap的用量低于45%。之后就会回到年轻代的gc。等到heap用量大于45%之后就会进入新的并发标记,然后多轮mixed collection。
参考
- Getting Started with the G1 Garbage Collector 【https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html】
- G1: One Garbage Collector To Rule Them All 【https://www.infoq.com/articles/G1-One-Garbage-Collector-To-Rule-Them-All/】