JVM调优篇-04

举报
kwan的解忧杂货铺 发表于 2024/08/29 19:46:06 2024/08/29
【摘要】 1.说说你对 G1 收集器的了解?Garbage First(简称 G1)收集器面向局部收集的设计思路和基于 Region 的内存布局形式。G1(Garbage-First)垃圾回收器是 Java HotSpot VM 的一种垃圾回收器,它于 Java 7u4 版本引入,并在 Java 9 及之后成为默认的垃圾回收器。G1 收集器是一种面向服务端应用的垃圾回收器,主要目标是提供更低的停顿时...

1.说说你对 G1 收集器的了解?

Garbage First(简称 G1)收集器面向局部收集的设计思路和基于 Region 的内存布局形式。

G1(Garbage-First)垃圾回收器是 Java HotSpot VM 的一种垃圾回收器,它于 Java 7u4 版本引入,并在 Java 9 及之后成为默认的垃圾回收器。G1 收集器是一种面向服务端应用的垃圾回收器,主要目标是提供更低的停顿时间和更高的吞吐量,以满足大内存堆的需求。

以下是 G1 垃圾回收器的主要特点和工作原理:

  1. 分代收集器:
    • G1 回收器也是一种分代垃圾回收器,将堆内存划分为新生代、老年代和永久代(或元空间)。
    • G1 主要关注于老年代的回收,而新生代的回收使用类似于其他收集器的方式,使用类似于 CMS 的并发标记-复制算法。
  2. 区域化的堆内存管理:
    • G1 将整个堆划分成大小相等的多个区域(Region),每个区域的大小可以配置。
    • 区域化的管理使得 G1 能够更加灵活地控制哪些区域进行垃圾回收,以便更快速地实现可预测的停顿时间。
  3. 并发标记-整理阶段:
    • G1 采用了并发标记-整理算法,在并发标记阶段,应用程序线程与垃圾回收线程并发执行,标记所有存活对象。
    • 在并发整理阶段,G1 将所有存活对象整理到一起,从而实现堆的整理和碎片的消除。
  4. 混合模式:
    • G1 采用混合模式(Mixed Mode),它允许在并发标记阶段选择部分区域进行标记,而在后续阶段只回收部分区域,从而控制回收的时间和延迟。
  5. 可预测的停顿时间:
    • G1 致力于实现可预测的停顿时间,通过控制回收的区域和时间划分,以及一些智能的启发式算法,使得 G1 的停顿时间相对稳定和可控。
  6. 大堆支持:
    • G1 适用于大内存堆,其优势在于更好的利用多核 CPU 和更少的 GC 暂停时间。

2.Region 堆内存布局的原理?

虽然 G1 也仍是遵循分代收集理论设计的,但其堆内存的布局与其他收集器有非常明显的差异: G1 不再坚持固定大小以及固定数量的分代区域划分,而是把连续的 Java 堆划分为多个大小相等的独立区域(Region),每一个 Region 都可以根据需要,扮演新生代的 Eden 空间、Survivor 空间,或者老年代空间。收集器能够对扮演不同角色的 Region 采用不同的策略去处理,这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。

Region 中还有一类特殊的 Humongous 区域,专门用来存储大对象。 G1 认为只要大小超过了一个 Region 容量一半的对象即可判定为大对象。每个 Region 的大小可以通过参数-XX: G1HeapRegionSize 设定,取值范围为 1MB ~ 32MB,且应为 2 的 N 次幂。而对于那些超过了整个 Region 容量的超级大对象,将会被存放在 N 个连续的 Humongous Region 之中, G1 的大多数行为都把 Humongous Region 作为老年代的一部分来进行看待。

虽然 G1 仍然保留新生代和老年代的概念,但新生代和老年代不再是固定的了,它们都是一系列区域(不需要连续)的动态集合。

3.G1 收集器可预测停顿时间模型?

G1 收集器之所以能建立可预测的停顿时间模型,是因为它将 Region 作为单次回收的最小单元,即每次收集到的内存空间都是 Region 大小的整数倍,这样可以有计划地避免在整个 Java 堆中进行全区域的垃圾收集。

更具体的处理思路是让 G1 收集器去跟踪各个 Region 里面的垃圾堆积的“价值”大小,价值即回收所获得的空间大小以及回收所需时间的经验值,然后在后台维护一个优先级列表,每次根据用户设定允许的收集停顿时间(使用参数-XX: MaxGCPauseMillis 指定,默认值是 200 毫秒),优先处理回收价值收益最大的那些 Region,这也就是“Garbage First”名字的由来。这种使用 Region 划分内存空间,以及具有优先级的区域回收方式,保证了 G1 收集器在有限的时间内获取尽可能高的收集效率。

4.跨 Region 引用问题,如何解决?

使用记忆集避免全堆作为 GC Roots 扫描,但在 G1 收集器上记忆集的应用其实要复杂很多,它的每个 Region 都维护有自己的记忆集,这些记忆集会记录下别的 Region 指向自己的指针,并标记这些指针分别在哪些卡页的范围之内。 G1 的记忆集在存储结构的本质上是一种哈希表, Key 是别的 Region 的起始地址, Value 是一个集合,里面存储的元素是卡表的索引号。这种“双向”的卡表结构(卡表是“我指向谁”,这种结构还记录了“谁指向我”)比原来的卡表实现起来更复杂,同时由于 Region 数量比传统收集器的分代数量明显要多得多,因此 G1 收集器要比其他的传统垃圾收集器有着更高的内存占用负担。根据经验, G1 至少要耗费大约相当于 Java 堆容量 10%至 20%的额外内存来维持收集器工作。

5.G1 收集器并发标记阶段?

如何保证用户线程和垃圾回收线程互不影响?

CMS 收集器采用增量更新算法实现,而 G1 收集器则是通过原始快照(SATB)算法来实现的。此外,垃圾收集对用户线程的影响还体现在回收过程中新创建对象的内存分配上,程序要继续运行就肯定会持续有新对象被创建, G1 为每一个 Region 设计了两个名为 TAMS (Top at Mark Start)的指针,把 Region 中的一部分空间划分出来用于并发回收过程中的新对象分配,并发回收时新分配的对象地址都必须要在这两个指针位置以上。 G1 收集器默认在这个地址以上的对象是被隐式标记过的,即默认它们是存活的,不纳入回收范围。与 CMS 中的“Concurrent Mode Failure”失败会导致 Full GC 类似,如果内存回收的速度赶不上内存分配的速度, G1 收集器也要被迫冻结用户线程执行,导致 Full GC 而产生长时间“Stop The World”。

6.G1 收集器垃圾回收的步骤?

  • 初始标记(Initial Marking):仅仅只是标记一下 GC Roots 能直接关联到的对象,并且修改 TAMS 指针的值,让下一阶段用户线程并发运行时,能正确地在可用的 Region 中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行 Minor GC 的时候同步完成的,所以 G1 收集器在这个阶段实际并没有额外的停顿。
  • 并发标记(Concurrent Marking):从 GC Root 开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理 SATB 记录下的在并发时有引用变动的对象。
  • 最终标记(Final Marking):对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的 SATB 记录。
  • 筛选回收(Live Data Counting and Evacuation):负责更新 Region 的统计数据,对各个 Region 的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个 Region 构成回收集,然后把决定回收的那一部分 Region 的存活对象复制到空的 Region 中,再清理掉整个旧 Region 的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。

从上述阶段的描述可以看出, G1 收集器除了并发标记外,其余阶段也是要完全暂停用户线程的

image-20231107001030937

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。