炸了!一口气问了我18个JVM问题!(中)

简介: 炸了!一口气问了我18个JVM问题!(中)

CMS GC 发生 concurrent mode failure 时的 full GC 为什么是单线程的?


以下的回答来自 R 大

因为没足够开发资源,偷懒了。就这么简单。没有任何技术上的问题。 大公司都自己内部做了优化。

所以最初怎么会偷这个懒的呢?多灾多难的CMS GC经历了多次动荡。它最初是作为Sun Labs的Exact VM的低延迟GC而设计实现的。

但 Exact VM在与 HotSpot VM争抢 Sun 的正牌 JVM 的内部斗争中失利,CMS GC 后来就作为 Exact VM 的技术遗产被移植到了 HotSpot VM上。

就在这个移植还在进行中的时候,Sun 已经开始略显疲态;到 CMS GC 完全移植到 HotSpot VM 的时候,Sun 已经处于快要不行的阶段了。

开发资源减少,开发人员流失,当时的 HotSpot VM 开发组能够做的事情并不多,只能挑重要的来做。而这个时候 Sun Labs 的另一个 GC 实现,Garbage-First GC(G1 GC)已经面世。

相比可能在长时间运行后受碎片化影响的 CMS,G1 会增量式的整理/压缩堆里的数据,避免受碎片化影响,因而被认为更具潜力。

于是当时本来就不多的开发资源,一部分还投给了把G1 GC产品化的项目上——结果也是进展缓慢。

毕竟只有一两个人在做。所以当时就没能有足够开发资源去打磨 CMS GC 的各种配套设施的细节,配套的备份 full GC 的并行化也就耽搁了下来。

但肯定会有同学抱有疑问:HotSpot VM不是已经有并行GC了么?而且还有好几个?

让我们来看看:

  • ParNew:并行的young gen GC,不负责收集old gen。
  • Parallel GC(ParallelScavenge):并行的young gen GC,与ParNew相似但不兼容;同样不负责收集old gen。
  • ParallelOld GC(PSCompact):并行的full GC,但与ParNew / CMS不兼容。

所以…就是这么一回事。

HotSpot VM 确实是已经有并行 GC 了,但两个是只负责在 young GC 时收集 young gen 的,这俩之中还只有 ParNew 能跟 CMS 搭配使用;

而并行 full GC 虽然有一个 ParallelOld,但却与 CMS GC 不兼容所以无法作为它的备份 full GC使用。


为什么有些新老年代的收集器不能组合使用比如 ParNew 和 Parallel Old?


image.png

这张图是 2008 年 HostSpot 一位 GC 组成员画的,那时候 G1 还没问世,在研发中,所以画了个问号在上面。

里面的回答是 :

"ParNew" is written in a style... "Parallel Old" is not written in the "ParNew" style

HotSpot VM 自身的分代收集器实现有一套框架,只有在框架内的实现才能互相搭配使用。

而有个开发他不想按照这个框架实现,自己写了个,测试的成绩还不错后来被  HotSpot VM 给吸收了,这就导致了不兼容。

我之前看到一个回答解释的很形象:就像动车组车头带不了绿皮车厢一样,电气,挂钩啥的都不匹配。


新生代的 GC 如何避免全堆扫描?


在常见的分代 GC 中就是利用记忆集来实现的,记录可能存在的老年代中有新生代的引用的对象地址,来避免全堆扫描。


image.png

image.png

上图有个对象精度的,一个是卡精度的,卡精度的叫卡表。

把堆中分为很多块,每块 512 字节(卡页),用字节数组来中的一个元素来表示某一块,1表示脏块,里面存在跨代引用。

image.png

image.png

cms 中需要记录老年代指向年轻代的引用,但是写屏障的实现并没有做任何条件的过滤

不判断当前对象是老年代对象且引用的是新生代对象才会标记对应的卡表为脏。

只要是引用赋值都会把对象的卡标记为脏,当然YGC扫描的时候只会扫老年代的卡表。

这样做是减少写屏障带来的消耗,毕竟引用的赋值非常的频繁。


那 cms 的记忆集和 G1 的记忆集有什么不一样?


cms 的记忆集的实现是卡表即 card table。

通常实现的记忆集是 points-out 的,我们知道记忆集是用来记录非收集区域指向收集区域的跨代引用,它的主语其实是非收集区域,所以是 points-out 的。

在 cms 中只有老年代指向年轻代的卡表,用于年轻代 gc。

而 G1 是基于 region 的,所以在 points-out 的卡表之上还加了个 points-into 的结构。

因为一个 region 需要知道有哪些别的 region 有指向自己的指针,然后还需要知道这些指针在哪些 card 中

其实 G1 的记忆集就是个 hash table,key 就是别的 region 的起始地址,然后 value 是一个集合,里面存储这 card table 的 index。

我们来看下这个图就很清晰了。

image.png

像每次引用字段的赋值都需要维护记忆集开销很大,所以 G1 的实现利用了 logging write barrier(下文会介绍)。

也是异步思想,会先将修改记录到队列中,当队列超过一定阈值由后台线程取出遍历来更新记忆集。


为什么 G1 不维护年轻代到老年代的记忆集?


G1 分了 young GC 和 mixed gc。

young gc 会选取所有年轻代的 region 进行收集。

midex gc 会选取所有年轻代的 region 和一些收集收益高的老年代 region 进行收集。

所以年轻代的 region 都在收集范围内,所以不需要额外记录年轻代到老年代的跨代引用



相关文章
|
5月前
|
算法 数据可视化 Java
深入理解JVM系列教程(完) - 终章总结
深入理解JVM系列教程(完) - 终章总结
35 0
|
7月前
|
存储 缓存 运维
JVM面试连环炮
JVM面试连环炮
70 0
|
8月前
|
存储 Java
java内存机制详解,老年人也看得懂
java内存机制详解,老年人也看得懂
53 0
|
9月前
|
存储 算法 Java
JVM 中的垃圾回收算法有啥门道吗?
JVM 中的垃圾回收算法有啥门道吗?
49 0
|
存储 监控 架构师
炸了!一口气问了我 18 个 JVM 问题
# 前言 GC 对于Java 来说重要性不言而喻,不论是平日里对 JVM 的调优还是面试中的无情轰炸。 这篇文章我会以一问一答的方式来展开有关 GC 的内容。 本文章所说的 GC 实现没有特殊说明的话,默认指的是 HotSpot 的。 我先将十八个问题都列出来,如果都清楚的话那就可以关闭这篇文章了。
191 0
炸了!一口气问了我 18 个 JVM 问题
JVM又曾放过谁,垃圾终将被回收
在Java中有一个很重要的概念,即一切皆对象。所谓对象,就是将现实中的事物抽象出来,进而可以通过继承、实现和组合的方式把万事万物都给容纳,所以理解对象的概念在学习Java(包括所有的面向对象的语言)的过程中至关重要。
|
存储 算法 安全
《我想进大厂》之JVM夺命连环10问
这是面试专题系列第五篇JVM篇。这一篇可能稍微比较长,没有耐心的同学建议直接拖到最后。
《我想进大厂》之JVM夺命连环10问
|
Java
烧点脑子使劲看--JVM运行时数据区详讲(上)
Java虚拟机在执行程序的过程会把它管理的内存划分为若干个不同的数据区。这些数据区有些是随着虚拟机进程的启动而一直存在的,有些区域则是依赖线程的启动和结束而创建和销毁的。
71 0
|
缓存 Java 编译器
烧点脑子使劲看--JVM运行时数据区详讲(下)
关于VM运行时数据区详讲(上)可以看我上篇的文章
89 0
炸了!一口气问了我18个JVM问题!(上)
炸了!一口气问了我18个JVM问题!(上)
炸了!一口气问了我18个JVM问题!(上)