「这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战」。
简述
Garbage First(简称G1)收集器是垃圾收集器技术发展史上的里程碑,它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式。
G1垃圾收集器主要面向服务端应用。G1垃圾收集器本来是被期望在短期内替换掉CMS垃圾收集器,不过由于CMS的代码和HotSpot的内存管理、执行、编译、监控等子系统都有千丝万缕的联系。所以在规划JDK10功能目标的时候HotSpot提出了统一垃圾收集器接口,将内存回收的行为和实现分离,垃圾收集器都重构成基于这套接口的一种实现。以此为基础日后要移除或者加入某一款收集器就会容易很多。
面向局部收集
G1垃圾收集器的设计者主要是为了实现一个停顿时间模型(指定M毫秒时间内,消耗在垃圾收集上的时间大概率不超过N毫秒)这样的目标。
具体的实现的话,G1收集器跳出了新生代和老年代的牢笼,而是面向堆内存的任何部分来组成回收集进行回收,衡量标准是哪块内存中存放的垃圾多,回收哪块内存的收益最大,这也称为G1收集器的Mixed GC模式。
基于Region的内存布局
G1收集器虽然也是根据分代收集理论设计的,但是其堆内存的布局与其他收集器有较大的差异。其把连续的Java堆划分成大小相等的独立区域,每个Region都可以当做新生代的Eden空间、Survivor空间或者老年代区域,收集器对扮演不同角色的区域采用不同的策略进行处理。
此外Region中还有一类Humongous区域,专门用来存储大对象,如果对象超过了一个Region则会用n个连续的Humongous Region来进行存储。
因为G1中将Region作为单次回收的最小单位,所以G1可以建立可预测的停顿时间模型。每次回收收集的都是单个Region的整数倍。
运行过程
首先先介绍下TAMS指针,该指针是在并发回收阶段,新分配的地址必须要在这两个指针位置上,G1收集器默认在这个地址以上的对象是被隐式标记过的,也就是默认他们是存活的,不纳入回收范围。
G1收集器的运行过程大致分为四个步骤:
-
初始标记:仅仅是标记下GC Root直接关联的对象,并修改TAMS指针的值。这个阶段需要暂停线程,不过耗时很短。
-
并发标记:从GC Root开始对堆中的对象进行可达性分析,递归扫描整个堆的对象图,找出要回收的对象,该阶段耗时很长,不过可以与用户线程并发执行。扫描完成后还要重新处理原始快照下有引用变化的对象。
-
最终标记:对用户线程进行一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的原始快照记录。
-
筛选回收:负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,并根据用户期望时间来制定回收计划,可以自由选择任何多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这个过程由多条收集器线程并行完成并需要暂停用户线程,因为有对象的移动。