面试题答案
一键面试CMS算法在高并发环境下的问题
- 浮动垃圾
- 问题描述:CMS在并发标记和清理阶段,应用程序仍在运行,这期间新产生的垃圾对象无法在本次垃圾回收中被处理,这些垃圾被称为浮动垃圾。如果浮动垃圾过多,可能导致老年代提前被填满,触发Full GC。
- 示例:假设在CMS并发标记阶段开始后,应用程序创建了大量临时对象,这些对象在标记结束前就不再被使用,但由于标记已经开始,它们不会被本次CMS回收。
- 并发模式失败
- 问题描述:CMS在并发清理阶段,若老年代剩余空间不足以分配新的对象,而此时CMS还未完成清理工作,就会发生并发模式失败。一旦发生,JVM会启动备用的Serial Old垃圾回收器进行Full GC,这会导致较长的停顿时间,严重影响应用程序性能。
- 示例:在高并发业务场景下,短时间内有大量对象晋升到老年代,而CMS清理速度跟不上对象进入老年代的速度,就容易出现这种情况。
- 内存碎片
- 问题描述:CMS采用标记 - 清除算法,在清理完垃圾对象后,会产生内存碎片。随着时间推移,内存碎片会越来越多,导致即使老年代整体空间足够,但由于碎片的存在,无法为大对象分配连续的内存空间,最终也可能触发Full GC。
- 示例:多次CMS回收后,老年代内存被碎片化,比如存在多个小块的空闲内存,但无法合并成一个大块来满足大对象的分配需求。
实际应用中的优化和调优方法
- 针对浮动垃圾
- 调整堆大小:适当增大堆的大小,特别是老年代的大小,为浮动垃圾提供更多的生存空间,减少因浮动垃圾导致老年代提前填满的可能性。例如,通过
-Xms
和-Xmx
参数调整堆初始大小和最大大小,通过-XX:OldSize
和-XX:MaxOldSize
参数调整老年代大小。 - 调整回收阈值:可以通过
-XX:CMSInitiatingOccupancyFraction
参数调整CMS垃圾回收的触发阈值。适当降低该阈值,使CMS更早地启动垃圾回收,减少浮动垃圾积累的时间和数量。例如,将该值从默认的68%适当调低到60%。
- 调整堆大小:适当增大堆的大小,特别是老年代的大小,为浮动垃圾提供更多的生存空间,减少因浮动垃圾导致老年代提前填满的可能性。例如,通过
- 针对并发模式失败
- 增加并发线程数:通过
-XX:ParallelCMSThreads
参数增加CMS垃圾回收的并发线程数,提高CMS的清理速度,减少并发模式失败的概率。但过多的线程会增加CPU竞争,需要根据实际情况调整。 - 优化对象分配:尽量优化应用程序的对象分配模式,减少短时间内大量对象晋升到老年代的情况。例如,通过合理设置新生代大小、调整晋升策略(如
-XX:MaxTenuringThreshold
参数),让对象尽量在新生代被回收,避免过早晋升到老年代。
- 增加并发线程数:通过
- 针对内存碎片
- 开启内存整理:可以通过
-XX:+UseCMSCompactAtFullCollection
参数开启在Full GC时进行内存整理,减少内存碎片。还可以通过-XX:CMSFullGCsBeforeCompaction
参数设置执行多少次Full GC后进行一次内存整理,例如设置为5,表示每5次Full GC后进行一次内存整理。 - 调整对象分配策略:尽量让对象按顺序分配,减少内存碎片化的可能性。例如,对于大对象,可以提前预分配,避免在运行时因内存碎片导致分配失败。
- 开启内存整理:可以通过