当前位置: 首页 > news >正文

JVM篇--垃圾回收器高频面试题

1 你知道哪几种垃圾收集器,各自的优缺点是啥,重点讲下cms和G1,包括原理,流程,优缺点?

1)首先简单介绍下 有以下这些垃圾回收器
Serial收集器单线程的收集器,收集垃圾时,必须stop the world,使用复制算法。
ParNew收集器Serial收集器的多线程版本,也需要stop the world,复制算法。
Parallel Scavenge收集器: 新生代收集器,复制算法的收集器,并发的多线程收集器,目标是达到一个可控的吞吐量。如果虚拟机总共运行100分钟,其中垃圾花掉1分钟,吞吐量就是99%。
Serial Old收集器: 是Serial收集器的老年代版本,单线程收集器,使用标记整理算法。
Parallel Old收集器: 是Parallel Scavenge收集器的老年代版本,使用多线程,标记-整理算法。
CMS(Concurrent Mark Sweep) 收集器是一种以获得最短回收停顿时间为目标的收集器,标记清除算法,运作过程:初始标记,并发标记,重新标记,并发清除,收集结束会产生大量空间碎片。

G1收集器标记整理算法实现,运作流程主要包括以下:初始标记,并发标记,最终标记,筛选标记。不会产生空间碎片,可以精确地控制停顿。(重点)

2)CMS收集器和G1收集器的区别:

  1. CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用;
  2. G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用;
  3. CMS收集器以最小的停顿时间为目标的收集器;
  4. G1收集器可预测垃圾回收的停顿时间
  5. CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
  6. G1收集器使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。

2详细介绍一下 CMS 垃圾回收器?

首先CMS垃圾收集器是一种
1 老年代的垃圾收集器
2 是以牺牲吞吐量为代价来获取最短回收停顿时间的垃圾回收器
3 多线程并发的标记-清除算法所以在gc的时候会产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行
垃圾清除,此时的性能将会被降低。

CMS 工作机制相比其他的垃圾收集器来说更复杂。
整个过程分为以下 4 个阶段:

初始标记
只是标记一下 GC Roots 能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。

并发标记
进行 GC Roots 跟踪的过程,和用户线程一起工作,不需要暂停工作线程。

重新标记
为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。

并发清理
清除 GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作, 所以总体上来看CMS 收集器的内存回收和用户线程是一起并发地执行。

3 详细介绍一下 G1 垃圾回收器?

首先G1垃圾收集器相比于CMS垃圾收集器,有两个突出的优点
1 基于标记整理算法,不产生内存碎片
2 可以按照按照用户预期的停顿时间进行执行,也就是说在不牺牲吞吐量的情况下,实现低停顿垃圾回收

G1垃圾回收器的实现原理:G1将Java堆划分为多个大小相等的独立区域(Region),一般Region大小等于堆大小除以2048,比如堆大小为4096M,则Region大小为2M,当然也可以用参数"-XX:G1HeapRegionSize"手动指定Region大小

G1垃圾收集器的特点:
1 使用了标记-整理算法,无内存碎片产生
2 无分代概念但是有内存分区策略,即G1它将新生代和老年代的这种物理空间划分取消了,但是G1 采取了内存分区策略,将堆内存划分为大小固定的几个独立区域。
而具体的分区 可以分为 新生代区域,老年代区域,大对象区域
G1垃圾收集器对于对象什么时候会转移到老年代跟之前讲过的原则一样,唯一不同的是对大对象的处理,G1有专门分配大对象的Region叫Humongous区,而不是让大对象直接进入老年代的Region中。在G1中,大对象的判定规则就是一个大对象超过了一个Region大小的50%,比如按照上面算的,每个Region是2M,只要一个大对象超过了1M,就会被放入Humongous中,
而且一个大对象如果太大,可能会横跨多个Region来存放。
Full GC的时候除了收集年轻代和老年代之外,也会将Humongous区一并回收

G1收集器一次GC的过程:(主要指Mixed GC)
初始标记暂停所有的其他线程,并记录下gc roots直接能引用的对象,速度很快 ;
并发标记:同CMS的并发标记
最终标记:同CMS的重新标记
筛选回收(Cleanup,STW):筛选回收阶段**1 首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿STW时间(可以用JVM参数 -XX:MaxGCPauseMillis指定)来制定回收计划,**比如说老年代此时有1000个Region都满了,但是因为根据预期停顿时间,本次垃圾回收可能只能停顿200毫秒,那么通过之前回收成本计算得知,可能回收其中800个Region刚好需要200ms,那么就只会回收800个Region(Collection Set,要回收的集合),说白了就是尽量把GC导致的停顿时间控制在我们指定的范围内
这个阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅提高收集效率。

4 新生代垃圾回收器和老年代垃圾回收器都有哪些?有什么区别?

新生代回收器:Serial、ParNew、Parallel Scavenge
老年代回收器:Serial Old、Parallel Old、CMS
整堆回收器:G1
新生代垃圾回收器一般采用的是复制算法
复制算法的优点是效率高,缺点是内存利用率低

老年代回收器一般采用的是标记-整理的算法进行垃圾回收。
要注意一点:老年代垃圾回收算法的速度至少比新生代垃圾回收算法的速度慢10倍。如果系统频繁的出现老年代中的full gc,会导致系统性能严重影响,出现频繁卡顿等情况
总结经验所谓JVM优化,就是尽可能让对象都在新生代里分配和回收,尽量别让太多对象频繁进入老年代,避免频繁对老年代进行垃圾回收,同时给系统充足的内存大小,避免新生代频繁的进行垃圾回收。

5 什么时候会触发FullGC?

1 首先我们要理解fullGC其实就是对老年代进行垃圾回收,同时也一般会对新生代进行垃圾回收;

2 那么做fullGC的目的是什么呢?
就是必须得把老年代里没人引用的对象给回收掉,这样才有可能让minor gc 后存活的对象进入到老年代

3因此我们就能推出哪些情况会触发FullGC?
除直接调用System.gc外,触发Full GC执行的情况有如下三种。
1. 年轻代中需要进行垃圾回收的对象过多。即在执行minor gc之前判断 老年代的内存空间小于新生代的所有对象总和,或者说也没有设置 “-XX: -HandlePromotionFailure” 这个参数,那么此时就会直接执行一次full gc,就是对老年代进行一次垃圾回收,(腾出一些内存空间,然后再执行minorGC );

2. 年轻代有大对象频繁进入到老年代
老生代空间只有在年轻代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:
java.lang.OutOfMemoryError: Java heap space
为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。

3. 永久代(元空间)已满
老年代的内存空间小于新生代的所有对象总和,或者说也没有设置 “-XX: -HandlePromotionFailure” 这个参数,那么此时就会直接执行一次full gc,就是对老年代进行一次垃圾回收,腾出一些内存空间,然后再执行minorGC

6 方法区如何进行垃圾回收?

方法区的垃圾收集主要回收两部分内容:1) 运行时常量池中废弃的常量;2 )不再使用的类型。
1 运行时常量池中废弃的常量:首先看常量 主要包括字面量和符号引用,其实只要常量池中的常量没有被任何地方引用,就可以被回收。
2 而一个类型的回收就包括:

  1. 该类所有的实例都已经被回收
  2. 加载该类的类加载器已经被回收
  3. 该类对应的java.lang.Class对象没有在任何地方被引用

7 Minor GC过后可能对应哪几种情况?

  1. 第一种可能,Minor GC过后,剩余的存活对象的大小,是小于Survivor区的大小的,那么此时存活对象进入Survivor区域即可。

  2. 第二种可能,Minor GC过后,剩余的存活对象的大小,是大于 Survivor区域的大小,但是是小于老年代可用内存大小的,此时就直接进入老年代即可。

  3. 第三种可能,很不幸,Minor GC过后,剩余的存活对象的大小,大于了Survivor区域的大小,也大于了老年代可用内存的大小。此时老年代都放不下这些存活对象了,就会发生“Handle Promotion Failure”的情况,这个时候就会触发一次“Full GC”。区域即可。的,此时就直接进入老年代即可。

8 哪些情况下Minor GC后的对象会进入老年代?

1 就是在新生代躲过15次gc的时候,就会进入到老年代
2 当进行minorGC 后,仍然存活的对象无法在servior区无法容纳,也会被转移到老年代

9 什么场景适合使用G1?

  1. 50%以上的堆被存活对象占用
  2. 对象分配和晋升的速度变化非常大
  3. 垃圾回收时间特别长,超过1秒
  4. 8GB以上的堆内存(建议值)
  5. 停顿时间是500ms以内

10 简单介绍下 zgc

ZGC收集器(-XX:+UseZGC)是一款在jdk11中引入的低延迟的垃圾回收器,它的特点主要包括
1 支持TB量级的堆。
2 最大GC停顿时间不超过10ms,之所以能做到这一点是因为它的停顿时间主要跟Root扫描有关,而Root数量和堆大小是没有任何关系的。
3 基于读屏障、 颜色指针等技术来实现可并发的标记-整理算法
在这里插入图片描述

ZGC存在的问题

ZGC最大的问题是浮动垃圾。因为 ZGC没有分代概念,每次都需要进行全堆扫描,导致一些“朝生夕死”的对象在一次的停顿时间内没能及时的被回收。
说白了虽然说ZGC的停顿时间是在10ms以下非常短,但是ZGC的执行时间还是远远大于这个时间的。假如ZGC全过程需要执行10分钟,在这个期间由于对象分配速率很高,将创建大量的新对象,这些对象很难进入当次GC,所以只能在下次GC的时候进行回收,这些只能等到下次GC才能回收的对象就是浮动垃圾。

11 如何选择垃圾收集器?

  1. 优先调整堆的大小让服务器自己来选择
  2. 如果内存小于100M,使用串行收集器
  3. 如果是单核,并且没有停顿时间的要求,串行或JVM自己选择
  4. 如果允许停顿时间超过1秒,选择并行或者JVM自己选
  5. 如果响应时间最重要,并且不能超过1秒,使用并发收集器
  6. 4G以下可以用parallel,4-8G可以用ParNew+CMS,8G以上可以用G1,几百G以上用ZGC

12 什么是三色标记法?

三色标记算法说白了 就是把 Gc roots可达性分析遍历对象过程中遇到的对象, 按照“是否访问过”这个条件标记成以下三种颜色:
黑色: 表示对象已经被垃圾收集器访问过, 且这个对象的所有引用都已经扫描过。 黑色的对象代表已经扫描过, 它是安全存活的, 如果有其他对象引用指向了黑色对象, 无须重新扫描一遍。 黑色对象不可能直接(不经过灰色对象) 指向某个白色对象。
灰色: 表示对象已经被垃圾收集器访问过, 但这个对象上至少存在一个引用还没有被扫描过
白色: 表示对象尚未被垃圾收集器访问过。 显然在可达性分析刚刚开始的阶段, 所有的对象都是白色的, 若在分析结束的阶段, 仍然是白色的对象, 即代表不可达。–即可达性分析算法还没有分析到该对象
在这里插入图片描述

13 那你知道多标和漏标吗?以及怎么解决呢?

多标:即在标记过程中有gcroot引用的对象之前被扫描过-即为非垃圾对象,但是由于在并发标记结束这部分局部变量被销毁了,这样一来本轮的gc就不会回收该对象,也就是原本应该被回收却没有回收的内存 也称为浮动垃圾,当然浮动垃圾不会对垃圾回收的正确性产生影响,只需要等到下一轮回收中被清理就行

漏标:指的是原本不是垃圾应该被标记为黑色,却没有被标记被当做垃圾进行了回收,这样一来其实就会对垃圾回收的正确性产生影响,而解决漏标有两种方式即增量更新和原始快照STAB
当然cms垃圾回收器和g1垃圾回收再解决漏标问题时的处理方式不同
cms用的是增量更新g1用的是原始快照
那么他们用的不同呢?这主要是因为增量更新它其实会做深度的扫描,也就是CMS会对增量引用的根对象做深度扫描,而cms之所以可以这样做,是因为他只有一块内存,但是g1不同,G1是有很多region组成,这样重新深度扫描对象的话G1的代价会比CMS高,所以G1选择SATB不深度扫描对象,只是简单标记,等到下一轮GC再深度扫描。这样效率也会更高

相关文章:

  • R语言【cli】——ansi_trimws():从ANSI字符串中删除前导和/或尾随空格
  • 链表|数据结构|C语言深入学习
  • c++设计模式之单例模式
  • 力扣(leetcode)第35题搜索插入位置(Python)
  • Git 操作
  • 启动低轨道卫星LEO通讯产业与6G 3GPP NTN标准
  • 纯前端网页编辑Office文档安全预览之打开Word文档后禁止另存为....
  • P1068 [NOIP2009 普及组] 分数线划定————C++、Python
  • HTML+CSS:飞翔按钮
  • 04 单链表
  • Go 爬虫之 colly 从入门到不放弃指南
  • HTTP 第二章 发展历史
  • 电工技术实验指导书-万用表的使用
  • Django随笔
  • linux 抓包
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • 【391天】每日项目总结系列128(2018.03.03)
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • angular学习第一篇-----环境搭建
  • ES10 特性的完整指南
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • Fundebug计费标准解释:事件数是如何定义的?
  • Kibana配置logstash,报表一体化
  • Linux gpio口使用方法
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • python3 使用 asyncio 代替线程
  • React中的“虫洞”——Context
  • Vue学习第二天
  • 诡异!React stopPropagation失灵
  • 开源SQL-on-Hadoop系统一览
  • 看域名解析域名安全对SEO的影响
  • 前端面试总结(at, md)
  • #{} 和 ${}区别
  • $.ajax()方法详解
  • (02)Cartographer源码无死角解析-(03) 新数据运行与地图保存、加载地图启动仅定位模式
  • (1/2)敏捷实践指南 Agile Practice Guide ([美] Project Management institute 著)
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (算法)Game
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • *2 echo、printf、mkdir命令的应用
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .Net 路由处理厉害了
  • .net 设置默认首页
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .Net接口调试与案例
  • /deep/和 >>>以及 ::v-deep 三者的区别
  • [<事务专题>]
  • [20170705]diff比较执行结果的内容.txt
  • [2023年]-hadoop面试真题(一)
  • [acwing周赛复盘] 第 69 场周赛20220917
  • [Android View] 可绘制形状 (Shape Xml)
  • [Android]使用Android打包Unity工程