jvm垃圾收集器你学废了吗(二)

前言

前面一篇文章讲了6种垃圾收集器分别是Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS(传送门
今天我们来说一下G1(Garbage First)收集器,为什么把G1收集器单独拿出来说呢?它是垃圾收集器技术发展历史上的里程碑式的成果,下面我们细细道来
jvm垃圾收集器你学废了吗(二)

G1收集器

G1收集器开创了收集器面向局部手机的设计思路和基于Region的内存布局形式,G1也是遵循分代收集理论的,但是它的内存布局有以下几点变化

  • G1不再使用固定大小以及固定数量的分代区域划分,而是把连续的java堆划分为多个大小相等的独立区域(Region),
  • 每个Region区域可根据需要扮演新生代和老年代
  • 收集器还能对不同角色的Region进行不同的策略处理
  • 如果大小超过Region的一半就认为是大对象

jvm垃圾收集器你学废了吗(二)

核心思想

G1收集器的核心思想就是,去追踪各个Region的价值(这里的价值就是指回收这块Region区可以回收多大的内存空间),在后台维护一个优先级表,然后按照用户设置的收集停顿时间去按优先级表一次回收,保证了G1收集器在有限的时间内获取尽可能高的收集效率

运作过程

G1的收集过程分为四个步骤:

  • 初始标记
    标记GC Roots能直接关联到的对象,并且修改TAMS指正的值,这个阶段需要停顿,但是时间很短
    TAMS指针是什么呢?G1收集器为每个Region设计两个TAMS指针,把一部分的Region区域划分出来,用户并发垃圾收集过程中用户线程分配新对象
  • 并发标记
    对堆中的内向进行可达性分析,递归的扫描整个对象图,并找出要回收的对象,这个阶段耗时很长,但是可以和用户线程并发执行
  • 最终标记
    在并发标记阶段,难免会有用户线程打破了原来的对象图结构,解决这个问题问题的方案就是原始快照,最终标记就是对原始快照进行标记,这个阶段也是需要短暂的暂停
  • 筛选回收
    对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间,选择性价比最高的Region区域进行回收。先讲Region存活的对象移动到新的Region区域中,然后对旧Region进行回收,这个阶段要进行对象的移动,所以必须要暂停用户线程。

3个核心问题

  • 既然G1将java堆分成了多个Region,那Region里存在这跨Region引用对象,G1是怎样解决的呢
    记忆集,每个Region区域会维护自己的记忆集,记忆集会记录下别的Region指向自己的指针,并标记这些指针分别在那些卡页中,G1的记忆集是一个键值的结构,key是别的Region的起始地址,value是一个集合,里面存放着卡表的索引号
  • 如果保证在并发标记阶段垃圾收集县线程和用户线程互不干扰
    原始快照,原始快照记录了那些对象的引用要被删除了,等并发标记完成之后,在重新扫描一下这些元素,
  • 如何建立可靠的停顿预测模式?
    用户可以通过-XX:MaxGCPauseMillis参数设置垃圾收集器的停顿时间,那如何来保证用户的期望呢,G1会记录每个Region的回收耗时和脏卡数量等,通过一系列的值来分析出平均状态,这里会提到一个衰减平均值,这是什么意思呢,衰减平均值跟准确的代表 最近的 平均状态,所以说G1根据这值来保证用户的停顿时间。

优点

  • G1从整体上看使用的是标记-整理算法,从局部上看使用的是标记-复制算法,所以不会产生内存碎片
  • 打破了原来的分代思想
  • 按Region的收益动态的确定回收集

缺点

  • 每个Region无论是新生代还是老年代,都是维护一个自己的卡表,这导致G1的记忆集占很大的堆空间

总结

G1收集器真的是很经典的收集器,也是JDK9默认的收集器,他的运行机制要比之前说到的复杂的多,看到这里你学废了吗?