Java虚拟机面试题:垃圾收集(下)

devtools/2025/2/12 5:35:52/

🧑 博主简介:CSDN博客专家历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
技术合作请加本人wx(注明来自csdn):foreast_sea

在这里插入图片描述


在这里插入图片描述

Java虚拟机面试题:垃圾收集(下)

31.知道哪些垃圾收集器?

JVM 的垃圾收集器主要分为两大类:分代收集器和分区收集器,分代收集器的代表是 CMS,分区收集器的代表是 G1 和 ZGC。

在这里插入图片描述
CMS 是第一个关注 GC 停顿时间(STW 的时间)的垃圾收集器,JDK 1.5 时引入,JDK9 被标记弃用,JDK14 被移除。

G1(Garbage-First Garbage Collector)在 JDK 1.7 时引入,在 JDK 9 时取代 CMS 成为了默认的垃圾收集器。

ZGC 是 JDK11 推出的一款低延迟垃圾收集器,适用于大内存低延迟服务的内存管理和回收,在 128G 的大堆下,最大停顿时间才 1.68 ms,性能远胜于 G1 和 CMS。

说说 Serial 收集器?

Serial 收集器是最基础、历史最悠久的收集器。

如同它的名字(串行),它是一个单线程工作的收集器,使用一个处理器或一条收集线程去完成垃圾收集工作。并且进行垃圾收集时,必须暂停其他所有工作线程,直到垃圾收集结束——这就是所谓的“Stop The World”。

Serial/Serial Old 收集器的运行过程如图:

在这里插入图片描述

说说 ParNew 收集器?

ParNew 收集器实质上是 Serial 收集器的多线程并行版本,使用多条线程进行垃圾收集。

ParNew/Serial Old 收集器运行示意图如下:

在这里插入图片描述

说说 Parallel Scavenge 收集器?

Parallel Scavenge 收集器是一款新生代收集器,基于标记-复制算法实现,也能够并行收集。和 ParNew 有些类似,但 Parallel Scavenge 主要关注的是垃圾收集的吞吐量——所谓吞吐量,就是 CPU 用于运行用户代码的时间和总消耗时间的比值,比值越大,说明垃圾收集的占比越小。

吞吐量

根据对象存活周期的不同会将内存划分为几块,一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

说说 Serial Old 收集器?

Serial Old 是 Serial 收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。

说说 Parallel Old 收集器?

Parallel Old 是 Parallel Scavenge 收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实现。

在这里插入图片描述

说说 CMS 收集器?

以获取最短回收停顿时间为目标,采用“标记-清除”算法,分 4 大步进行垃圾收集,其中初始标记和重新标记会 STW,JDK 1.5 时引入,JDK9 被标记弃用,JDK14 被移除。

在这里插入图片描述

说说 Garbage First 收集器?

G1(Garbage-First Garbage Collector)在 JDK 1.7 时引入,在 JDK 9 时取代 CMS 成为了默认的垃圾收集器。G1 有五个属性:分代、增量、并行、标记整理、STW。

有梦想的肥宅:G1

说说 ZGC 收集器?

ZGC 是 JDK 11 时引入的一款低延迟的垃圾收集器,最大特点是将垃圾收集的停顿时间控制在 10ms 以内,即使在 TB 级别的堆内存下也能保持较低的停顿时间。

它通过并发标记和重定位来避免大部分 Stop-The-World 停顿,主要依赖指针染色来管理对象状态。

得物技术

  • 标记对象的可达性:通过在指针上增加标记位,不需要额外的标记位即可判断对象的存活状态。
  • 重定位状态:在对象被移动时,可以通过指针染色来更新对象的引用,而不需要等待全局同步。

适用于需要超低延迟的场景,比如金融交易系统、电商平台。

垃圾回收器的作用是什么?

垃圾回收器的核心作用是自动管理 Java 应用程序的运行时内存。它负责识别哪些内存是不再被应用程序使用的(即“垃圾”),并释放这些内存以便重新使用。

这一过程减少了程序员手动管理内存的负担,降低了内存泄漏和溢出错误的风险。

32.能详细说一下 CMS 收集器的垃圾收集过程吗?

在这里插入图片描述

CMS(Concurrent Mark Sweep)主要使用了标记-清除算法进行垃圾收集,分 4 大步:

  • 初始标记Initial Mark):标记所有从 GC Roots 直接可达的对象,这个阶段需要 STW,但速度很快。
  • 并发标记Concurrent Mark):从初始标记的对象出发,遍历所有对象,标记所有可达的对象。这个阶段是并发进行的,STW。
  • 重新标记Remark):完成剩余的标记工作,包括处理并发阶段遗留下来的少量变动,这个阶段通常需要短暂的 STW 停顿。
  • 并发清除Concurrent Sweep):清除未被标记的对象,回收它们占用的内存空间。
你提到了remark,那它remark具体是怎么执行的?三色标记法?

是的,remark 阶段通常会结合三色标记法来执行,确保在并发标记期间所有存活对象都被正确标记。目的是修正并发标记阶段中可能遗漏的对象引用变化。

在 remark 阶段,垃圾收集器会停止应用线程(STW),以确保在这个阶段不会有引用关系的进一步变化。这种暂停通常很短暂。remark 阶段主要包括以下操作:

  1. 处理写屏障记录的引用变化:在并发标记阶段,应用程序可能会更新对象的引用(比如一个黑色对象新增了对一个白色对象的引用),这些变化通过写屏障记录下来。在 remark 阶段,GC 会处理这些记录,确保所有可达对象都正确地标记为灰色或黑色。
  2. 扫描灰色对象:再次遍历灰色对象,处理它们的所有引用,确保引用的对象正确标记为灰色或黑色。
  3. 清理:确保所有引用关系正确处理后,灰色对象标记为黑色,白色对象保持不变。这一步完成后,所有存活对象都应当是黑色的。
什么是三色标记法?

Java全栈架构师:三色标记法

三色标记法用于标记对象的存活状态,它将对象分为三类:

  1. 白色(White):尚未访问的对象。垃圾回收结束后,仍然为白色的对象会被认为是不可达的对象,可以回收。
  2. 灰色(Gray):已经访问到但未标记完其引用的对象。灰色对象是需要进一步处理的。
  3. 黑色(Black):已经访问到并且其所有引用对象都已经标记过。黑色对象是完全处理过的,不需要再处理。

三色标记法的工作流程:

①、初始标记(Initial Marking):从 GC Roots 开始,标记所有直接可达的对象为灰色。

②、并发标记(Concurrent Marking):在此阶段,标记所有灰色对象引用的对象为灰色,然后将灰色对象自身标记为黑色。这个过程是并发的,和应用线程同时进行。

此阶段的一个问题是,应用线程可能在并发标记期间修改对象的引用关系,导致一些对象的标记状态不准确。

③、重新标记(Remarking):重新标记阶段的目标是处理并发标记阶段遗漏的引用变化。为了确保所有存活对象都被正确标记,remark 需要在 STW 暂停期间执行。

④、**使用写屏障(Write Barrier)**来捕捉并发标记阶段应用线程对对象引用的更新。通过遍历这些更新的引用来修正标记状态,确保遗漏的对象不会被错误地回收。

33.G1 垃圾收集器了解吗?

G1 在 JDK 1.7 时引入,在 JDK 9 时取代 CMS 成为默认的垃圾收集器。

有梦想的肥宅:G1 收集器

G1 把 Java 堆划分为多个大小相等的独立区域Region,每个区域都可以扮演新生代(Eden 和 Survivor)或老年代的角色。

同时,G1 还有一个专门为大对象设计的 Region,叫 Humongous 区。

大对象的判定规则是,如果一个大对象超过了一个 Region 大小的 50%,比如每个 Region 是 2M,只要一个对象超过了 1M,就会被放入 Humongous 中。

这种区域化管理使得 G1 可以更灵活地进行垃圾收集,只回收部分区域而不是整个新生代或老年代。

G1 收集器的运行过程大致可划分为这几个步骤:

①、并发标记,G1 通过并发标记的方式找出堆中的垃圾对象。并发标记阶段与应用线程同时执行,不会导致应用线程暂停。

②、混合收集,在并发标记完成后,G1 会计算出哪些区域的回收价值最高(也就是包含最多垃圾的区域),然后优先回收这些区域。这种回收方式包括了部分新生代区域和老年代区域。

选择回收成本低而收益高的区域进行回收,可以提高回收效率和减少停顿时间。

③、可预测的停顿,G1 在垃圾回收期间仍然需要「Stop the World」。不过,G1 在停顿时间上添加了预测机制,用户可以 JVM 启动时指定期望停顿时间,G1 会尽可能地在这个时间内完成垃圾回收。

在这里插入图片描述

34.有了 CMS,为什么还要引入 G1?

特性CMSG1
设计目标低停顿时间可预测的停顿时间
并发性
内存碎片是,容易产生碎片否,通过区域划分和压缩减少碎片
收集代数年轻代和老年代整个堆,但区分年轻代和老年代
并发阶段并发标记、并发清理并发标记、并发清理、并发回收
停顿时间预测较难预测可配置停顿时间目标
容易出现的问题内存碎片、Concurrent Mode Failure较少出现长时间停顿

CMS 适用于对延迟敏感的应用场景,主要目标是减少停顿时间,但容易产生内存碎片。G1 则提供了更好的停顿时间预测和内存压缩能力,适用于大内存和多核处理器环境。

35.你们线上用的什么垃圾收集器?为什么要用它?

我们生产环境中采用了设计比较优秀的 G1 垃圾收集器,因为它不仅能满足低停顿的要求,而且解决了 CMS 的浮动垃圾问题、内存碎片问题。

G1 非常适合大内存、多核处理器的环境。

以上是比较符合面试官预期的回答,但实际上,大多数情况下我们可能还是使用的 JDK 8 默认垃圾收集器。

可以通过以下命令查看当前 JVM 的垃圾收集器:

java">java -XX:+PrintCommandLineFlags -version

二哥的 Java 进阶之路:JDK 默认垃圾收集器

UseParallelGC = Parallel Scavenge + Parallel Old,表示新生代用Parallel Scavenge收集器,老年代使用Parallel Old 收集器。

因此你也可以这样回答:我们系统的业务相对复杂,但并发量并不是特别高,所以我们选择了适用于多核处理器、能够并行处理垃圾回收任务,且能提供高吞吐量的Parallel GC

但这个说法不讨喜,你也可以回答:

我们系统采用的是 CMS 收集器,能够最大限度减少应用暂停时间。

工作中项目使用的什么垃圾回收算法?

我们生产环境中采用了设计比较优秀的 G1 垃圾收集器,G1 采用的是分区式标记-整理算法,将堆划分为多个区域,按需回收,适用于大内存和多核环境,能够同时考虑吞吐量和暂停时间。

或者:

我们系统采用的是 CMS 收集器,CMS 采用的是标记-清除算法,能够并发标记和清除垃圾,减少暂停时间,适用于对延迟敏感的应用。

再或者:

我们系统采用的是 Parallel 收集器,Parallel 采用的是年轻代使用复制算法,老年代使用标记-整理算法,适用于高吞吐量要求的应用。

36.垃圾收集器应该如何选择?

垃圾收集器的选择需要权衡的点还是比较多的——例如运行应用的基础设施如何?使用 JDK 的发行商是什么?等等……

这里简单地列一下上面提到的一些收集器的适用场景:

  • Serial :如果应用程序有一个很小的内存空间(大约 100 MB)亦或它在没有停顿时间要求的单线程处理器上运行。
  • Parallel:如果优先考虑应用程序的峰值性能,并且没有时间要求要求,或者可以接受 1 秒或更长的停顿时间。
  • CMS/G1:如果响应时间比吞吐量优先级高,或者垃圾收集暂停必须保持在大约 1 秒以内。
  • ZGC:如果响应时间是高优先级的,或者堆空间比较大。

http://www.ppmy.cn/devtools/158122.html

相关文章

命令行参数和环境变量

目录 命令行参数环境变量 命令行参数 main函数的参数可带可不带 可带&#xff1a;这些参数的意义&#xff1a;int argc, char *argv[] 1 #include <stdio.h>2 #include <unistd.h>3 4 int main(int argc, char *argv[])5 {6 int i;7 for(i 0; i < arg…

前端技术学习——ES6核心基础

1、ES6与JavaScript之间的关系 ES6是JavaScript的一个版本&#xff1a;JavaScript是基于ECMAScript规范实现的编程语言&#xff0c;而ES6&#xff08;ECMAScript 2015&#xff09;是该规范的一个具体版本。 2、ES6的基础功能 &#xff08;1&#xff09;let和const let用于声明…

详细代码篇:python+mysql +h5实现基于的电影数据统计分析系统实战案例(二)

点击阅读原文&#xff1a;: 详细代码篇&#xff1a;pythonmysql h5实现基于的电影数据统计分析系统实战案例&#xff08;二&#xff09;&#xff01; https://mp.weixin.qq.com/s/h-w875AMJLeQ-NLdrDoEzg?token910031714&langzh_CN1、先按照目录结构创建对应的文件夹及文…

使用css3锥形渐变conic-gradient实现有趣样式

在之前的篇幅中介绍过css的线性渐变linear-gradient()和径向渐变radial-gradient()&#xff0c;如果你对这两种渐变还不了解的话&#xff0c;可以看一下之前录制的视频教程。 往期文档地址&#xff1a;https://blog.csdn.net/qq_18798149/article/details/134389038 视频学习地…

Java 反射机制的安全隐患与防范措施:在框架开发与代码审计中的应用

前言 在 Java 编程的广阔领域中&#xff0c;反射机制堪称一把神奇且强大的钥匙&#xff0c;它为开发者打开了通往动态编程的全新大门。借助反射&#xff0c;Java 程序获得了在运行时自我审视和操作的独特能力&#xff0c;极大地增强了代码的灵活性与适应性。 简单来讲&#x…

github - 使用

注册账户以及创建仓库 要想使用github第一步当然是注册github账号了, github官网地址:https://github.com/。 之后就可以创建仓库了(免费用户只能建公共仓库),Create a New Repository,填好名称后Create,之后会出现一些仓库的配置信息,这也是一个git的简单教程。 Git…

【C/C++】每日温度 [ 栈的应用 ] 蓝桥杯/ACM备赛

数据结构考点&#xff1a;栈 题目描述&#xff1a; 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0…

Scala语言的系统运维

Scala语言的系统运维 引言 在今天的科技发展时代&#xff0c;软件系统的复杂性和规模不断增加&#xff0c;因此系统运维的管理和监控显得尤为重要。在众多编程语言中&#xff0c;Scala因其高度的表达力和强大的性能而受到越来越多开发者和运维人员的青睐。本文将探讨Scala语言…