前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试官:G1 GC 是什么?

面试官:G1 GC 是什么?

作者头像
业余草
发布2021-12-06 17:45:29
2.4K0
发布2021-12-06 17:45:29
举报
文章被收录于专栏:业余草业余草

解答

代码语言:javascript
复制
G1(Garbage First)是一个横跨新生代和老年代的垃圾收集器。

实际上,它已经打乱了新生代和老年代的堆结构,直接将堆分成极其多个区域。每个区域都可以充当 Eden 区、Survivor 区或者老年代中的一个。

它采用的是标记 - 压缩算法,而且和 CMS 一样都能够在应用程序运行过程中并发地进行垃圾回收。

G1 能够针对每个细分的区域来进行垃圾回收。在选择进行垃圾回收的区域时,它会优先回收死亡对象较多的区域。这也是 G1 名字的由来。

G1 GC的内部结构

从内存区域的角度,G1 同样存在着年代的概念,但是与前面在堆内存划分中讲的很不一样,其内部是类似棋盘状的一个个 region 组成,请参考下面的示意图。

G1-GC的内部结构

region 的大小是一致的,数值是在 1M 到 32M 字节之间的一个 2 的幂值数,JVM 会尽量划分 2048 个左右、同等大小的 region。

当然这个数字既可以手动调整,G1 也会根据堆大小自动进行调整。

在 G1 实现中,年代是个逻辑概念,具体体现在,一部分 region 是作为 Eden,一部分作为 Survivor。

除了 Old region,G1 会将超过 region 50% 大小的对象(在应用中,通常是 byte 或 char 数组)归类为 Humongous 对象,并放置在相应的 region 中。

逻辑上,Humongous region 算是老年代的一部分,因为复制这样的大对象是很昂贵的操作,并不适合新生代 GC 的复制算法。

region 设计有什么缺陷?

region 大小和大对象很难保证一致,这会导致空间的浪费。

上面示意图中有的区域是 Humongous 颜色,但没有用名称标记,这是为了表示,特别大的对象是可能占用超过一个 region 的。

并且,region 太小不合适,会令你在分配大对象时更难找到连续空间,这是一个长久存在的情况。

这本质也可以看作是 JVM 的 bug,尽管解决办法也非常简单,直接设置较大的 region 大小,参数如下:

代码语言:javascript
复制
-XX:G1HeapRegionSize=<N, 例如16>M

垃圾回收算法

G1 选择的是复合算法,可以简化理解为:

  1. 在新生代,G1 采用的仍然是并行的复制算法,所以同样会发生 Stop-The-World 的暂停。
  2. 在老年代,大部分情况下都是并发标记,而整理(Compact)则是和新生代 GC 时捎带进行,并且不是整体性的整理,而是增量进行的。

G1 GC中的分代收集

习惯上人们喜欢把新生代 GC(Young GC)叫作 Minor GC,老年代 GC 叫作 Major GC,区别于整体性的 Full GC。

但是现代 GC 中,这种概念已经不再准确,对于 G1 来说:

  1. Minor GC 仍然存在,虽然具体过程会有区别,会涉及 Remembered Set 等相关处理。
  2. 老年代回收,则是依靠 Mixed GC。并发标记结束后,JVM 就有足够的信息进行垃圾收集,Mixed GC 不仅同时会清理 Eden、Survivor 区域,而且还会清理部分 Old 区域。

可以通过设置下面的参数,指定触发阈值,并且设定最多被包含在一次 Mixed GC 中的 region 比例。

代码语言:javascript
复制
–XX:G1MixedGCLiveThresholdPercent
–XX:G1OldCSetRegionThresholdPercent

Remembered Set(记忆集)

Remembered Set,用于「记录和维护 region 之间对象的引用关系」

为什么需要这么做呢?

试想,新生代 GC 是复制算法,也就是说,类似对象从 Eden 或者 Survivor 到 to 区域的“移动”,其实是“复制”,本质上是一个新的对象。

在这个过程中,需要必须保证老年代到新生代的跨区引用仍然有效。下面的示意图说明了相关设计。

RememberedSet

G1 的很多开销都是源自 Remembered Set,例如,它通常约占用 Heap 大小的 20% 或更高,这可是非常庞大的比例。

并且,我们进行对象复制的时候,因为需要扫描和更改 Card Table 的信息,这个速度影响了复制的速度,进而影响暂停时间。

Card Table(卡表)

前面提到的记忆集其实是一种"抽象"的数据结构,而卡表就是记忆集的一种具体实现,它定义了记忆集的记录精度,与堆内存的映射关系等。

「GC 最早引入卡表的目的是为了对内存的引用关系做标记,从而根据引用关系快速遍历活跃对象。」

该技术将整个堆划分为一个个大小为 512 字节的卡,并且维护一个卡表,用来存储每张卡的一个标识位。

这个标识位代表对应的卡是否可能存有指向新生代对象的引用。如果可能存在,那么我们就认为这张卡是脏的。

在进行 Minor GC 的时候,我们便可以不用扫描整个老年代,而是在卡表中寻找脏卡,并将脏卡中的对象加入到 Minor GC 的 GC Roots 里。

当完成所有脏卡的扫描之后,Java 虚拟机便会将所有脏卡的标识位清零。

由于 Minor GC 伴随着存活对象的复制,而复制需要更新指向该对象的引用。

因此,在更新引用的同时,我们又会设置引用所在的卡的标识位。

这个时候,我们可以确保脏卡中必定包含指向新生代对象的引用。

如何优化 G1 GC?

  1. 建议尽量升级到较新的 JDK 稳定版本.
  2. 掌握 GC 调优信息收集途径。掌握尽量全面、详细、准确的信息,是各种调优的基础,不仅仅是 GC 调优。

和 G1 GC 相关的日志配置

除了常用的两个选项,

代码语言:javascript
复制
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps

还有一些非常有用的日志选项,很多特定问题的诊断都是要依赖这些选项:

代码语言:javascript
复制
-XX:+PrintAdaptiveSizePolicy // 打印G1 Ergonomics相关信息

我们知道 GC 内部一些行为是适应性的触发的,利用 PrintAdaptiveSizePolicy,我们就可以知道为什么 JVM 做出了一些可能我们不希望发生的动作。

例如,G1 调优的一个基本建议就是避免进行大量的 Humongous 对象分配,如果 Ergonomics 信息说明发生了这一点,那么就可以考虑要么增大堆的大小,要么直接将 region 大小提高。

如果是怀疑出现引用清理不及时的情况,则可以打开下面选项,掌握到底是哪里出现了堆积。

代码语言:javascript
复制
-XX:+PrintReferenceGC

另外,建议开启选项下面的选项进行并行引用处理。

代码语言:javascript
复制
-XX:+ParallelRefProcEnabled

需要注意的一点是,JDK 9 中 JVM 和 GC 日志机构进行了重构,其实我前面提到的 PrintGCDetails 已经被标记为废弃,而 PrintGCDateStamps 已经被移除,指定它会导致 JVM 无法启动。

可以使用下面的命令查询新的配置参数。

代码语言:javascript
复制
java -Xlog:help

通用实践

  1. 如果发现 Young GC 非常耗时,这很可能就是因为新生代太大了,我们可以考虑减小新生代的最小比例。
代码语言:javascript
复制
-XX:G1NewSizePercent
  1. 降低新生代最大值同样对降低 Young GC延迟有帮助
代码语言:javascript
复制
-XX:G1MaxNewSizePercent
  1. 如果我们直接为 G1 设置较小的延迟目标值,也会起到减小新生代的效果,虽然会影响吞吐量。

如果是 Mixed GC 延迟较长,我们应该怎么做呢?

部分 Old region 会被包含进 Mixed GC,减少一次处理的 region 个数,就是一个直接的选择之一。

上面已经介绍了 G1OldCSetRegionThresholdPercent 控制其最大值,

代码语言:javascript
复制
–XX:G1OldCSetRegionThresholdPercent

还可以利用下面参数提高 Mixed GC 的个数,当前默认值是 8,Mixed GC 数量增多,意味着每次被包含的 region 减少。

代码语言:javascript
复制
-XX:G1MixedGCCountTarget

❝ 注意避免过度调优,G1 对大堆非常友好,其运行机制也需要浪费一定的空间,有时候稍微多给堆一些空间,比进行苛刻的调优更加实用。 ❞

总的来说,G1 的调优相对简单、直观,因为可以直接设定暂停时间等目标,并且其内部引入了各种智能的自适应机制。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-07-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 解答
  • G1 GC的内部结构
  • region 设计有什么缺陷?
  • 垃圾回收算法
  • G1 GC中的分代收集
  • Remembered Set(记忆集)
  • Card Table(卡表)
  • 如何优化 G1 GC?
    • 和 G1 GC 相关的日志配置
    • 通用实践
      • 如果是 Mixed GC 延迟较长,我们应该怎么做呢?
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档