垃圾收集器3-G1收集器
G1收集器是当今收集器技术发展最前沿的成果之一,与其他收集器相比,G1收集器具备一下特点:
-
并行与并发:其他部分收集器原本需要停顿Java线程执行的GC动作,G1收集器任然可以通过并发的方式让Java线程继续运行。
-
分代收集:虽然G1收集器可以不需要其他收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
-
空间整合:G1收集器运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。
-
可预测的停顿:G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片断内,消耗在垃圾收集上的时间不得超过N毫秒。
G1收集器将Java堆划分为多个大小相等的独立区域(Region)。它之所以能建立可预测的停顿时间模型,是因为它可以有计划的避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的来及堆积的价值大小(回收所获得的空间大小及回收所需要的时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间优先收集价值最大的Region。
G1收集器中Region之间的对象引用以及其他收集器中的新生代和老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描的。G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中(在分代的例子中就是检查是否老年代的对象引用了新生代中的对象),如果是,便通过CardTable把相关引用信息记录到都被引用对象所属的Region的Remembered Set中。当进行内存回收时没在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。
G1收集器的运作划分为以下步骤:
- 初始标记:标记一下GC Roots能直接关联到的对象,且修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行的同时,能在正确可用的Region中穿件新对象,该阶段需要停顿线程,但耗时很短。
- 并发标记:对堆中对象进行可达性分析,找出存活的对象,耗时较长,但可与用户线程并发执行。
- 最终标记:为了修正并发标记期间因用户程序继续运行而导致标记产生变化的那一部分标记记录,虚拟机将这段时间内对象变化记录在Remembered Set Logs里,该阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但可以并行执行。
- 筛选回收:首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。
运行示意图如下: