面试官 100% 会严刑拷打的 CMS 垃圾回收器,下次面试就拿这篇文章怼回去!
重磅资讯、干货,第一时间送达
今日推荐:
这里跟大家讲个面试的最常见的垃圾回收器的问题,我跟大伙说,你不用怀疑,CMS垃圾回收器一定是最常见的问题,只要问到了Java虚拟机,面试官恨不得就问你CMS,当然还有就是G1这个垃圾回收器了,所以,关于这个垃圾回收器的细节问题,一定要掌握好,只要掌握到位,那么一定可以让面试官满意。
但是,说句糟心的话,运气不好,面试官就是不对眼,也是没有办法的事情,只能认栽,自我感觉再良好,也只是自我感觉,在面试官心里,你就是渣渣!!!
好了,下面我们开始面试环节,这篇文章想换一种方式,我们列举一些面试常见的问题,然后再来回答这些问题。
正文
这个春天,因为疫情的原因,所有的面试都是线上远程面试的,所以,如果运气好,你可以看到面试官的脸,如果运气不好,你可以只能被面试官看到你紧张的样子,而你,看到的只是黑屏,哈哈!
这就是最真实的场景!
可是,并没有面试官的身影,只有一次又一次的毒打!!
小伙子,你说一下 CMS 垃圾回收器吧!
这个题目一来,吓出一身冷汗,差点就没有复习这个CMS,还好昨晚抱佛脚看了一下哈。
于是我。。。一顿操作猛如虎。
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是基于“标记-清除”算法实现的,并且常见的应用场景是互联网站或者B/S系统的服务端上的Java应用。
结果就一紧张就记得这么多,面试官肯定不满意了,这个时候,面试官的常规操作是,继续严刑拷打,他想,你可能忘记了,我来提醒提醒你!
CMS收集器工作的整个流程是怎么样的,你能给我讲讲吗?
这个时候,面试官还会安慰你说不用紧张,但是,安慰归安慰,最后挂不挂可是另一回事。
于是,我又开始回答问题。
CMS 处理过程有七个步骤:
-
初始标记,会导致stw; -
并发标记,与用户线程同时运行; -
预清理,与用户线程同时运行; -
可被终止的预清理,与用户线程同时运行; -
重新标记 ,会导致swt; -
并发清除,与用户线程同时运行;
其实,只要回答四个就差不多了,是这几个。
-
初始标记:仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,需要“Stop The World”。 -
并发标记:进行GC Roots Tracing的过程,在整个过程中耗时最长。 -
重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。此阶段也需要“Stop The World”。 -
并发清除。
你以为这样子就可以了,面试官就会说可以了,如果可以了,那估计你凉了!
面试官说:CMS这么好,那有没有什么缺点呢?
我。。。好吧,谁怪我这么强呢,对吧。
其实,CMS虽然经过这么些年的考验,已经是一个值得信赖的GC回收器了,但是,其实也是有一些他的不足的,
第一,垃圾碎片的问题,我们都知道CMS是使用的是标记-清除算法的,所以不可避免的就是会出现垃圾碎片的问题。第二,一般CMS的GC耗时80%都在remark阶段,remark阶段停顿时间会很长,在CMS的这四个主要的阶段中,最费时间的就是重新标记阶段。第三,concurrent mode failure,说出这个的时候,面试官就会觉得,小伙子,哎呦,不错哟,掌握的比较清楚,那这个是什么意思呢,其实是说:
这个异常发生在cms正在回收的时候。执行CMS GC的过程中,同时业务线程也在运行,当年轻带空间满了,执行ygc时,需要将存活的对象放入到老年代,而此时老年代空间不足,这时CMS还没有机会回收老年带产生的,或者在做Minor GC的时候,新生代救助空间放不下,需要放入老年代,而老年代也放不下而产生的。
第四,promotion failed,这个问题是指,在进行Minor GC时,Survivor空间不足,对象只能放入老年代,而此时老年代也放不下造成的,多数是由于老年代有足够的空闲空间,但是由于碎片较多,新生代要转移到老年带的对象比较大,找不到一段连续区域存放这个对象导致的。
面试官看到你掌握的这么好,心里已经给你竖起来大拇指,但是,面试官觉得你优秀啊,就还想看看你到底还有多少东西。
既然你知道有这么多的缺点,那么你知道怎么解决这些问题吗?
这个真的被问蒙了,你以为我什么都会吗!!!!
但是,我还是得给大家讲讲,不然下次被问到,可能会把锅甩给我。
-
垃圾碎片的问题:针对这个问题,这时候我们需要用到这个参数:
-XX:CMSFullGCsBeforeCompaction=n
意思是说在上一次CMS并发GC执行过后,到底还要再执行多少次full GC
才会做压缩。默认是0,也就是在默认配置下每次CMS GC顶不住了而要转入full GC的时候都会做压缩。 -
concurrent mode failure
解决这个问题其实很简单,只需要设置两个参数即可
-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60:是指设定CMS在对内存占用率达到60%的时候开始GC。
为什么设置这两个参数呢?由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集。
当然也不能设置过高,比如90%,这时候虽然GC次数少,但是,却会导致用于用户线程空间小,效率不高,太低10%,你自己想想会怎么样,体会体会!
哈哈,万事大吉,这一点说出了,估计面试官已经爱上我了吧,赶紧把我招进去干活吧。。。
-
remark阶段停顿时间会很长的问题:解决这个问题巨简单,加入 -XX:+CMSScavengeBeforeRemark
。在执行remark操作之前先做一次Young GC
,目的在于减少年轻代对老年代的无效引用,降低remark时的开销。
结尾
面到这里,面试官给你说了一句:小伙子很优秀,思考问题很深入,什么时候可以来我们公司实习,我们公司转正几率很高啊,欢迎您的加入!
https://github.com/OUYANGSIHAI/JavaInterview
这么辛苦总结,给个star好不好。 点击阅读原文,直达