【Java】浅(xN)谈CMS为什么会使用

【Java】浅(xN)谈CMS为什么会使用"标记-清除"?

Scroll Down

PS:本人JVM小白,可能写得不太对,有不足之处欢迎指出,不喜勿喷哦~

前段时间刚好读了一些JVM的书/博客,于是就被朋友问到连环炮了一回:jdk14有啥新玩意儿知道吗?废除了CMS知道吗?那你能说说为什么CMS会使用"标记清除"

当时听到最后一个问题我是比较懵的,平时只知道"标记-清除"算法容易造成过多的内存碎片,导致大对象无法分配时触发Full GC等问题,但从未想过为什么CMS会使用它?这个问题我也百度、Google过,但得到的答复总感觉说不到点上。


最近脑袋灵光一闪,想到了一点,自我感觉还挺对的,于是就现在这里记录一下。

首先,站在现在的角度看CMS,即便它的实现再烂,甚至烂到jdk14宣布废弃了,但我们仍不能忘记,它是首个实现并发回收的垃圾收集器,说它是并发回收的里程碑一点也不为过。所以要了解它为什么要使用"标记-清除"算法,站在现在的时间刻度看肯定是不行的。

于是,我们可以将时间退回到CMS刚出生的时候,也就是jdk1.5版本的时候,看看到底是什么因素导致CMS必须使用"标记-清除"这种大部分时候都被嗤之以鼻的算法。

时间倒退之后,我们必须再次明确,CMS的设计目标:低延迟、并发回收

明确了它的设计目标后,再看看当时JVM的一些结构,我很快就找到原因之一了:GC堆的内存结构

我们可以看看G1(jdk1.7)之前的垃圾收集器的GC堆内存结构是什么样子的(见下图):
jdk早期垃圾收集器gc堆内存布局.png

上图中,我们能明显的发现,无论是年轻代、老年代还是永久代,它们的内存区域都是固定的

内存区域的固定会带来什么问题呢?不要忘了,并发回收是用户线程和回收线程一起工作的,这里关键在于用户线程,如果回收线程随便改变对象的内存地址,那么用户线程想获取对象的时候就可能获取不到了。而无论是"标记-整理"还是"复制"算法都会导致对象地址的改变,而在jdk1.5的时候,既没有Region的概念,也没有转发指针、染色指针等概念,为了尽量不改变(改变了就得STW)对象的地址,就只好采用"标记-清除算法了"。


当然除此之外还有可能很多原因,只是以我目前的水平还get不到,如果有路过的大神知道的话欢迎补充~