Garbage First 收集器(G1)中的概念
介绍
G1 设计目的
在JVM 性能分析—— 一文带你读懂 CMS 垃圾收集器收集流程介绍了 CMS 垃圾收集器的工作流程,CMS 收集器的关注点是低延迟,尽可能缩短垃圾收集时用户线程的停顿时间,但在 JDK 9 之后,CMS 不推荐使用。取而代之是 Garbage First,即 G1 垃圾回收器,它除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在垃圾收集上的时间不得超过 N 毫秒。
每次根据用户设定允许的收集停顿时间(使用参数 -XX:MaxGCPauseMillis 指定,默认值是200毫秒),优先处理回收价值收益最大的那些 Region, 这也就是 “Garbage First” 名字的由来。这种使用 Region 划分内存空间,以及具有优先级的区域回收方式,保证了G1收集器在有限的时间内获取尽可能高的收集效率。
G1 设计思想
在 G1 收集器出现之前的所有其他收集器,包括 CMS 在内,垃圾收集的目标范围要么是整个新生代(Minor GC),要么就是整个老年代(Major GC),再要么就是整个 Java 堆(Full GC)。而 G1 跳出了这个樊笼,G1 把堆内存分割为很多不相关的区域(Region,物理上不连续区分,逻辑上是连续区分 Eden 区、Survivor区,old区),Region 作为单次回收的最小单元,它可以面向堆内存任何部分来组成回收集(Collection Set,一般简称 CSet)进行回收,衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是 G1 收集器的 Mixed GC模式。基于 Region 的堆内存布局是它能够实现这个目标的关键,GC 从传统的连续堆内存布局逐渐走向了不连续内存块布局(物理上不连续区分 Eden 区、Survivor区,old区)。
Region:实现可预测的停顿时间模型、Mixed GC 的基础
G1 把堆内存分割为很多不相关的区域,使用不同的 Region 来表示Eden、幸存者0区,幸存者1区,老年代等。Region 中还有一类特殊的 Humongous 区域,专门用来存储大对象。G1 认为只要大小超过了一个 Region 容量一半的对象即可判定为大对象。每个 Region 的大小可以通过参数 -XX:G1HeapRegionSize 设定,取值范围为 1MB~32MB。 而对于那些超过了整个 Region 容量的超级大对象, 将会被存放在 N 个连续的 Humongous Region 之中, 相加后只要能够确保总大小可以存放这个大对象,就会分配给这个大对象。如果没有能够找到符合条件的连续可用 Region,那么 G1 只能执行一次 Full GC。G1 的大多数行为都把 Humongous Region 作为老年代的一部分来进行看待。
G1 对于大对象有特殊的分配方式。一个大对象是指该对象的大小超过一个 Region 大小的 50% 以上。这个大小包括了Java对象头。
CSet (Collection Set) :存放待回收的 Region 集合
CSet 代表了在一次 G1 垃圾回收过程中,需要被回收的区域集合。
- G1 会将一些 Eden 区域和 Survivor 区域(From 区和 To 区)纳入 CSet 进行回收。除了年轻代区域,G1 还会将一些老年代区域纳入 CSet 进行回收。
- G1 收集器会根据当前堆的使用情况动态调整 CSet 中年轻代区域和老年代区域的比例。通常情况下,CSet 中年轻代区域的比例会较高,因为年轻代区域通常包含更多的垃圾对象。
RSet (Remembered Set):记录对象之间的引用关系
RSet 用于跟踪对象之间的引用关系,是由 G1 收集器维护的一种数据结构,用于记录跨区域的对象引用关系。
- 每个 Region 区域都会有一个对应的 RSet,记录着该区域中对象引用其他区域中对象的情况。
- 当应用程序发生对象引用变化时,G1 收集器会实时更新相应区域的 RSet。这个更新过程通常是在应用程序的写屏障(write barrier)中进行的,以确保 RSet 的及时性和准确性。
- 在 G1 的混合回收过程中,RSet 用于快速定位需要被复制的对象。通过 RSet,G1 可以快速找到跨区域引用的对象,避免全堆扫描,提高