大厂面试题:有了 G1 还需要其他垃圾回收器吗?

news2025/1/19 17:01:01

Java全能学习+面试指南:https://javaxiaobear.cn
今天我们主要来看下这两个高频的面试考题:

  • G1 的回收原理是什么?为什么 G1 比传统 GC 回收性能好?

  • 为什么 G1 如此完美仍然会有 ZGC?

我们在上一篇中,简要的介绍了 CMS 垃圾回收器,下面我们简单回忆一下它的一个极端场景(而且是经常发生的场景)。

在发生 Minor GC 时,由于 Survivor 区已经放不下了,多出的对象只能提升(promotion)到老年代。但是此时老年代因为空间碎片的缘故,会发生 concurrent mode failure 的错误。这个时候,就需要降级为 Serail Old 垃圾回收器进行收集。这就是比 concurrent mode failure 更加严重的 promotion failed 问题。

一次简单的 Major GC,竟然能演化成耗时最长的 Full GC。最要命的是,这个停顿时间是不可预知的。

有没有一种办法,能够首先定义一个停顿时间,然后反向推算收集内容呢?就像是领导在年初制定 KPI 一样,分配的任务多就多干些,分配的任务少就少干点。

很久之前就有领导教导过我,如果你列的目标太大,看起来无法完成,不要怕。有一个叫作里程碑的名词,可以让我们以小跑的姿态,完成一次马拉松。

G1 的思路说起来也类似,它不要求每次都把垃圾清理的干干净净,它只是努力做它认为对的事情。

我们要求 G1,在任意 1 秒的时间内,停顿不得超过 10ms,这就是在给它制定 KPI。G1 会尽量达成这个目标,它能够推算出本次要收集的大体区域,以增量的方式完成收集。

这也是使用 G1 垃圾回收器不得不设置的一个参数:

-XX:MaxGCPauseMillis=10

为什么叫 G1

G1 的目标是用来干掉 CMS 的,它同样是一款软实时垃圾回收器。相比 CMS,G1 的使用更加人性化。比如,CMS 垃圾回收器的相关参数有 72 个,而 G1 的参数只有 26 个。

G1 的全称是 Garbage­First GC,为了达成上面制定的 KPI,它和前面介绍的垃圾回收器,在对堆的划分上有一些不同。

其他的回收器,都是对某个年代的整体收集,收集时间上自然不好控制。G1 把堆切成了很多份,把每一份当作一个小目标,部分上目标很容易达成。

那又有一个面试题来啦:G1 有年轻代和老年代的区分吗?

如图所示,G1 也是有 Eden 区和 Survivor 区的概念的,只不过它们在内存上不是连续的,而是由一小份一小份组成的。

这一小份区域的大小是固定的,名字叫作小堆区(Region)。小堆区可以是 Eden 区,也可以是 Survivor 区,还可以是 Old 区。所以 G1 的年轻代和老年代的概念都是逻辑上的。

每一块 Region,大小都是一致的,它的数值是在 1M 到 32M 字节之间的一个 2 的幂值数。

但假如我的对象太大,一个 Region 放不下了怎么办?注意图中有一块面积很大的黄色区域,它的名字叫作 Humongous Region,大小超过 Region 50% 的对象,将会在这里分配。

Region 的大小,可以通过参数进行设置:

-XX:G1HeapRegionSize=<N>M

那么,回收的时候,到底回收哪些小堆区呢?是随机的么?

这当然不是。事实上,垃圾最多的小堆区,会被优先收集。这就是 G1 名字的由来。

G1 的垃圾回收过程

在逻辑上,G1 分为年轻代和老年代,但它的年轻代和老年代比例,并不是那么“固定”,为了达到 MaxGCPauseMillis 所规定的效果,G1 会自动调整两者之间的比例。

如果你强行使用 -Xmn 或者 -XX:NewRatio 去设定它们的比例的话,我们给 G1 设定的这个目标将会失效。

G1 的回收过程主要分为 3 类:

(1)G1“年轻代”的垃圾回收,同样叫 Minor GC,这个过程和我们前面描述的类似,发生时机就是 Eden 区满的时候。

(2)老年代的垃圾收集,严格上来说其实不算是收集,它是一个“并发标记”的过程,顺便清理了一点点对象。

(3)真正的清理,发生在“混合模式”,它不止清理年轻代,还会将老年代的一部分区域进行清理。

在 GC 日志里,这个过程描述特别有意思,(1)的过程,叫作 [GC pause (G1 Evacuation Pause) (young),而(2)的过程,叫作 [GC pause (G1 Evacuation Pause) (mixed)。Evacuation 是转移的意思,和 Copy 的意思有点类似。

这三种模式之间的间隔也是不固定的。比如,1 次 Minor GC 后,发生了一次并发标记,接着发生了 9 次 Mixed GC。

RSet

RSet 是一个空间换时间的数据结构。

在第 6 课时中,我们提到过一个叫作卡表(Card Table)的数据结构,用来解决跨代引用的问题。RSet 的功能与此类似,它的全称是 Remembered Set,用于记录和维护 Region 之间的对象引用关系。

但 RSet 与 Card Table 有些不同的地方。Card Table 是一种 points-out(我引用了谁的对象)的结构。而 RSet 记录了其他 Region 中的对象引用本 Region 中对象的关系,属于 points-into 结构(谁引用了我的对象),有点倒排索引的味道。

你可以把 RSet 理解成一个 Hash,key 是引用的 Region 地址,value 是引用它的对象的卡页集合。

有了这个数据结构,在回收某个 Region 的时候,就不必对整个堆内存的对象进行扫描了。它使得部分收集成为了可能。

对于年轻代的 Region,它的 RSet 只保存了来自老年代的引用,这是因为年轻代的回收是针对所有年轻代 Region 的,没必要画蛇添足。所以说年轻代 Region 的 RSet 有可能是空的。

而对于老年代的 Region 来说,它的 RSet 也只会保存老年代对它的引用。这是因为老年代回收之前,会先对年轻代进行回收。这时,Eden 区变空了,而在回收过程中会扫描 Survivor 分区,所以也没必要保存来自年轻代的引用。

RSet 通常会占用很大的空间,大约 5% 或者更高。不仅仅是空间方面,很多计算开销也是比较大的。

事实上,为了维护 RSet,程序运行的过程中,写入某个字段就会产生一个 post-write barrier 。为了减少这个开销,将内容放入 RSet 的过程是异步的,而且经过了很多的优化:Write Barrier 把脏卡信息存放到本地缓冲区(local buffer),有专门的 GC 线程负责收集,并将相关信息传给被引用 Region 的 RSet。

参数 -XX:G1ConcRefinementThreads 或者 -XX:ParallelGCThreads 可以控制这个异步的过程。如果并发优化线程跟不上缓冲区的速度,就会在用户进程上完成。

具体回收过程

G1 还有一个 CSet 的概念。这个就比较好理解了,它的全称是 Collection Set,即收集集合,保存一次 GC 中将执行垃圾回收的区间(Region)。GC 是在 CSet 中的所有存活数据(Live Data)都会被转移。

了解了上面的数据结构,我们再来简要看一下回收过程。

年轻代回收

年轻代回收是一个 STW 的过程,它的跨代引用使用 RSet 数据结构来追溯,会一次性回收掉年轻代的所有 Region。

JVM 启动时,G1 会先准备好 Eden 区,程序在运行过程中不断创建对象到 Eden 区,当所有的 Eden 区都满了,G1 会启动一次年轻代垃圾回收过程。

   ![](https://img-blog.csdnimg.cn/img_convert/95d9895832b94389e2780a499e03f836.jpeg)      

年轻代的收集包括下面的回收阶段:

(1)扫描根

根,可以看作是我们前面介绍的 GC Roots,加上 RSet 记录的其他 Region 的外部引用。

(2)更新 RS

处理 dirty card queue 中的卡页,更新 RSet。此阶段完成后,RSet 可以准确的反映老年代对所在的内存分段中对象的引用。可以看作是第一步的补充。

(3)处理 RS

识别被老年代对象指向的 Eden 中的对象,这些被指向的 Eden 中的对象被认为是存活的对象。

(4)复制对象

没错,收集算法依然使用的是 Copy 算法。

在这个阶段,对象树被遍历,Eden 区内存段中存活的对象会被复制到 Survivor 区中空的 Region。这个过程和其他垃圾回收算法一样,包括对象的年龄和晋升,无需做过多介绍。

(5)处理引用

处理 Soft、Weak、Phantom、Final、JNI Weak 等引用。结束收集。

它的大体示意图如下所示。

并发标记(Concurrent Marking)

当整个堆内存使用达到一定比例(默认是 45%),并发标记阶段就会被启动。这个比例也是可以调整的,通过参数 -XX:InitiatingHeapOccupancyPercent 进行配置。

Concurrent Marking 是为 Mixed GC 提供标记服务的,并不是一次 GC 过程的一个必须环节。这个过程和 CMS 垃圾回收器的回收过程非常类似,你可以类比 CMS 的回收过程看一下。具体标记过程如下:

(1)初始标记(Initial Mark)

这个过程共用了 Minor GC 的暂停,这是因为它们可以复用 root scan 操作。虽然是 STW 的,但是时间通常非常短。

(2)Root 区扫描(Root Region Scan)

(3)并发标记( Concurrent Mark)

这个阶段从 GC Roots 开始对 heap 中的对象标记,标记线程与应用程序线程并行执行,并且收集各个 Region 的存活对象信息。

(4)重新标记(Remaking)

和 CMS 类似,也是 STW 的。标记那些在并发标记阶段发生变化的对象。

(5)清理阶段(Cleanup)

这个过程不需要 STW。如果发现 Region 里全是垃圾,在这个阶段会立马被清除掉。不全是垃圾的 Region,并不会被立马处理,它会在 Mixed GC 阶段,进行收集。

了解 CMS 垃圾回收器后,上面这个过程就比较好理解。但是还有一个疑问需要稍微提一下。

如果在并发标记阶段,又有新的对象变化,该怎么办?

这是由算法 SATB 保证的。SATB 的全称是 Snapshot At The Beginning,它作用是保证在并发标记阶段的正确性。

这个快照是逻辑上的,主要是有几个指针,将 Region 分成个多个区段。如图所示,并发标记期间分配的对象,都会在 next TAMS 和 top 之间。

混合回收(Mixed GC)

能并发清理老年代中的整个整个的小堆区是一种最优情形。混合收集过程,不只清理年轻代,还会将一部分老年代区域也加入到 CSet 中。

通过 Concurrent Marking 阶段,我们已经统计了老年代的垃圾占比。在 Minor GC 之后,如果判断这个占比达到了某个阈值,下次就会触发 Mixed GC。这个阈值,由 -XX:G1HeapWastePercent 参数进行设置(默认是堆大小的 5%)。因为这种情况下, GC 会花费很多的时间但是回收到的内存却很少。所以这个参数也是可以调整 Mixed GC 的频率的。

还有参数 G1MixedGCCountTarget,用于控制一次并发标记之后,最多执行 Mixed GC 的次数。

ZGC

你有没有感觉,在系统切换到 G1 垃圾回收器之后,线上发生的严重 GC 问题已经非常少了?

这归功于 G1 的预测模型和它创新的分区模式。但预测模型也会有失效的时候,它并不是总如我们期望的那样运行,尤其是你给它定下一个苛刻的目标之后。

另外,如果应用的内存非常吃紧,对内存进行部分回收根本不够,始终要进行整个 Heap 的回收,那么 G1 要做的工作量就一点也不会比其他垃圾回收器少,而且因为本身算法复杂了,还可能比其他回收器要差。

所以垃圾回收器本身的优化和升级,从来都没有停止过。最新的 ZGC 垃圾回收器,就有 3 个令人振奋的 Flag:

  1. 停顿时间不会超过 10ms;

  2. 停顿时间不会随着堆的增大而增大(不管多大的堆都能保持在 10ms 以下);

  3. 可支持几百 M,甚至几 T 的堆大小(最大支持 4T)。

在 ZGC 中,连逻辑上的年轻代和老年代也去掉了,只分为一块块的 page,每次进行 GC 时,都会对 page 进行压缩操作,所以没有碎片问题。ZGC 还能感知 NUMA 架构,提高内存的访问速度。与传统的收集算法相比,ZGC 直接在对象的引用指针上做文章,用来标识对象的状态,所以它只能用在 64 位的机器上。

现在在线上使用 ZGC 的还非常少。即使是用,也只能在 Linux 平台上使用。等待它的普及,还需要一段时间。

小结

我们简要看了下 G1 垃圾回收器的回收过程,并着重看了一下底层的数据结构 RSet。基本思想很简单,但实现细节却特别多。这不是我们的重点,对 G1 详细过程感兴趣的,可以参考纸质书籍。

相对于 CMS,G1 有了更可靠的驾驭度。而且有 RSet 和 SATB 等算法的支撑,Remark 阶段更加高效。

G1 最重要的概念,其实就是 Region。它采用分而治之,部分收集的思想,尽力达到我们给它设定的停顿目标。

G1 的垃圾回收过程分为三种,其中,并发标记阶段,为更加复杂的 Mixed GC 阶段做足了准备。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1007694.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flask+pyecharts结合,html统计图呈现在前端页面

是我,是你 在网上看到这样一段话: “很多时候,你必须接受这世界上突如其来的失去。洒了的牛奶,遗失的钱包,走散的爱人,断掉的友情。当你做什么都于事无补的时候,唯一能做的,就是让自己努力好过一点。” <

iptables 目标地址转换

目录 一、实验准备 二、配置web服务器 三、配置web防火墙网卡 四、配置客户机网卡 五、测试 1、开启防火墙功能&#xff0c;设置源地址转换&#xff0c;通过改变我客户机的地址身份为web服务器同网段来实现访问 2、通过改变目标地址&#xff08;客户机&#xff09;的地址…

无法加载文件xxx,此系统上禁止运行脚本

在 VsCode 跑一个前端项目的时候出现如下错误&#xff1a;此系统上禁止运行脚本 解决&#xff1a;在终端执行指令设置 脚本执行策略 为 RemoteSigned 以管理员身份运行vs code&#xff1b;查看当前执行策略&#xff1a;get-ExecutionPolicy&#xff0c;如显示 Restricted 则表…

element UI table横向树结合checkbox进行多选,实现各个节点的[全选,半选,不选]状态附带模拟数据

element UI table横向树结合checkbox进行多选&#xff0c;实现各个节点的[全选&#xff0c;半选&#xff0c;不选]状态&#xff0c;包含模拟数据 思路&#xff1a; 步骤一、后端返回tree格式数据&#xff0c;先结合element-ui的table的数据格式要求&#xff0c;将tree转换成ta…

第30章_瑞萨MCU零基础入门系列教程之IRDA红外遥控实验

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

苹果iPhone 15/Pro新机发布,毫米波5G仍然只限美国版

苹果公司今日发布了 iPhone 15 系列新机&#xff0c;共四款&#xff0c;分别是 iPhone 15、iPhone 15 Plus、iPhone 15 Pro 和 iPhone 15 Pro Max。这些新机型都配备了 USB-C 接口和灵动岛&#xff0c;而 Pro 版还有更多的特色功能&#xff0c;如 A17 Pro 芯片、轻质钛金属框架…

2023.2.1最新版IDEA创建一个SpringBoot项目,简单小示例

介绍 Springboot Spring Boot_百度百科 新建项目 打开IDEA选择 File->New->Project&#xff1b;选择 Spring Initializr Spring initializr 是Spring 官方提供的一个用来初始化一个Spring boot 项目的工具。组名项目名称可自定义 点击 next 选择 Dependencies Web…

芯科蓝牙BG27开发笔记1-新建示例工程

此笔记的必要性 芯科的官方资料很丰富&#xff0c;并且ssv5中能方便索引到所需文档&#xff0c;不过大而全的问题就是找不到合适的切入点&#xff0c;更不会有本地化比较好的中文的系统的教程了。往往看到一个starting guide&#xff0c;会延伸其他starting guide&#xff0c;…

ChatGPT高级数据分析功能

目录 只需要上传数据集就可以自动化分析,我们测试以下,首先我找了一份数据,主要是关于二手车的,格式如下: 接下来调用,GPT中的高级数据分析功能,上传数据,并要求进行分析 第一步:自动对数据字段进行详细的解释: 第二步,对数据进行预处理,比如缺失值,基本的描述…

第29章_瑞萨MCU零基础入门系列教程之改进型环形缓冲区

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

C语言数组和指针笔试题(一)(一定要看)

目录 一维数组例题1例题2例题3例题4例题5例题6例题7例题8例题9例题10例题输出结果 字符数组一例题1例题2例题3例题4例题5例题6例题7 一维数组 int a[] {1,2,3,4}; 1:printf("%d\n",sizeof(a)); 2:printf("%d\n",sizeof(a0)); 3:printf("%d\n",…

TypeScript高级类型

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 映射类型&#xff08;Mapped Types&#xff09; 1. Readonly 2. Partial 3. Pick 4. Record 条件类型&#xf…

【javaSE】 反射与反射的使用

文章目录 &#x1f332;反射的定义&#x1f38d;反射的用途&#x1f334;反射基本信息&#x1f340;反射相关的类&#x1f6a9;Class类(反射机制的起源 )&#x1f388;Class类中的相关方法 &#x1f6a9;反射示例&#x1f388;获得Class对象的三种方式&#x1f388;反射的使用 …

导数应用:曲线的凹凸性、渐进线、弧微分与曲率

目录 曲线的凹凸性 函数的拐点 曲线的渐近线 函数的弧微分与曲率 曲线的凹凸性 曲线的凹凸性是描述曲线在某一点处的曲率属性的几何性质。 具体来说&#xff0c;对于平面上的曲线&#xff0c;其在某一点的切线的斜率是不断变化的。当切线的斜率在某区间内恒为正值时&#…

基于HTML、CSS和JavaScript制作一个中秋节倒计时网页

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 这个项目使用HTML、CSS和…

第一百四十天学习记录:工作相关:Qt5.14.2的安装

最近一段时间被新项目搞得心力憔悴。 加上需要调用一个同事的OpenCV库一直报错&#xff0c;后面发现是Qt版本不兼容的问题。 在尝试了在线安装Qt无果后&#xff0c;重新安装Qt&#xff0c;顺便回顾一下Qt的安装过程。 毕竟上一次安装Qt还是在2019年……之后的这4年工作电脑和家…

TypeScript类型推断

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 类型推断 1. 基础类型推断 #2. 最佳公共类型推断 3. 上下文类型推断 4. 类型断言 #5. 类型推断和泛型 总结 类…

【javaSE】 Lambda表达式与Lambda表达式的使用

文章目录 &#x1f333;Lambda表达式的背景&#x1f6a9;Lambda表达式的语法&#x1f6a9;函数式接口 &#x1f38b;Lambda表达式的基本使用&#x1f384;语法精简 &#x1f332;变量捕获&#x1f6a9;匿名内部类&#x1f6a9;匿名内部类的变量捕获&#x1f6a9;Lambda的变量捕…

【力扣-二叉树-01】在二叉树中分配硬币-力扣 979 题

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

多层全连接网络:实现手写数字识别50轮准确率92.1%

多层全连接网络&#xff1a;实现手写数字识别50轮准确率92.1% 1 导入必备库2 torchvision内置了常用数据集和最常见的模型3 数据批量加载4 绘制样例5 创建模型7 设置是否使用GPU8 设置损失函数和优化器9 定义训练函数10 定义测试函数11 开始训练12 绘制损失曲线并保存13 绘制准…