百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

字节、阿里面试都在问的CMS GC问题:9个你一定会遇到的GC问题

yund56 2025-06-17 02:43 18 浏览

"生产环境Full GC频繁,RT超过3秒,该如何优化?" "面试官:讲讲你对CMS GC的理解?" "为什么堆内存还有40%,却频繁发生GC?"

如果你也曾被这些问题困扰,那这篇文章正是为你准备的。




开篇:一个真实的生产事故

凌晨3点,运维紧急电话打来:

"线上订单系统响应超时,监控面板一片飘红..."

登录系统一看,触目惊心:

  • GC频率: 2分钟一次Full GC
  • RT: P99从200ms飙升到3000ms
  • CPU: GC线程占用80%
  • 内存: Old区使用率不断攀升

这是一个典型的GC问题,但背后的原因可能有很多:

  • 内存泄漏?
  • 代码缺陷?
  • GC参数配置不当?
  • 还是别的什么原因?




让我们通过9个真实案例,深入探究CMS GC的各种问题场景,建立系统的GC问题诊断和优化方法。

为什么要写这篇文章?

在我10年+的Java开发生涯中,遇到最多的就是GC问题。每次面试必问的也是GC。但网上的GC文章要么太浅,要么太深,很少有完整的案例分析。

这篇文章将从实战出发:

  • 9个真实案例剖析
  • 每个案例都包含问题表现、底层原因、最佳实践
  • 配合源码级讲解
  • 完整的优化方案

无论你是初学者还是老手,都能从这篇文章中获得成长。

阅读指南

本文较长(预计阅读时间30分钟),建议:

  1. 先看目录,选择感兴趣的场景
  2. 准备好JVM工具:jstat、jmap、MAT等
  3. 动手实践,准备测试程序
  4. 收藏本文,便于日后查阅

让我们开始这段探索之旅吧!



九种CMS GC问题分析与解决方案详解

一、动态扩容引起的空间震荡

问题表现

  • 服务刚启动时GC次数较多
  • 最大空间有剩余但仍发生GC
  • GC Cause显示"Allocation Failure"
  • 每次GC后堆内存空间会被调整

底层原因



// JVM动态扩容的核心代码
void ConcurrentMarkSweepGeneration::compute_new_size() {
    // 如果增量收集失败,直接扩容到最大值
    if (incremental_collection_failed()) {
        grow_to_reserved();
        return;
    }
    
    // 根据当前使用情况计算新的大小
    CardGeneration::compute_new_size();
}

问题在于:

  • -Xms和-Xmx设置不一致
  • 初始化时只分配-Xms大小空间
  • 每次空间不足时要向OS申请内存
  • 申请内存过程中必然触发GC

解决方案

  1. 设置-Xms和-Xmx相等,避免动态扩容
  2. 合理设置-XX:MinHeapFreeRatio和-XX:MaxHeapFreeRatio
  3. 预估容量,给JVM足够的初始空间



二、显式GC的去与留

问题表现

  • System.gc()导致的Full GC
  • 频繁GC但每次回收量不大
  • GC日志中出现"System.gc()"

底层原因

public static void gc() {
    boolean shouldRunGC;
    synchronized(LOCK) {
        shouldRunGC = justRanFinalization;
        if (shouldRunGC) {
            justRanFinalization = false;
        } else {
            runFinalization();
        }
    }
    if (shouldRunGC) {
        runGC();
    }
}

显式GC的问题:

  • 会触发Full GC,STW时间长
  • 打断CMS的并发收集
  • 影响系统的吞吐量

解决方案

  1. 通过-XX:+DisableExplicitGC禁用System.gc()
  2. 使用-XX:+ExplicitGCInvokesConcurrent将System.gc()转为CMS GC
  3. 代码优化,避免调用System.gc()




三、MetaSpace区OOM

问题表现

  • MetaSpace空间耗尽
  • 出现OOM异常
  • 伴随着频繁的Full GC

底层原因



// MetaSpace的类加载机制
class ClassLoader {
    protected Class<?> loadClass(String name) 
        throws ClassNotFoundException {
        // 1. 查找已加载的类
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            // 2. 尝试加载新类
            c = findClass(name);
            // 3. 存储到MetaSpace
            defineClass(name, b, 0, b.length);
        }
        return c;
    }
}

MetaSpace问题常见原因:

  • 动态生成类导致类元数据信息过多
  • ClassLoader泄漏
  • 反射、动态代理使用不当

解决方案

  1. 设置合理的MetaSpace大小
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m
  1. 排查类加载和卸载情况
  2. 检查是否存在ClassLoader泄漏
  3. 合理使用动态代理等反射机制



四、过早晋升

问题表现

  • Young GC频繁
  • 对象过早进入老年代
  • 老年代空间增长过快

底层原因



// 动态年龄计算
if (age < MaxTenuringThreshold) {
    // 计算当前age及以下的对象总大小
    size_t total = 0;
    for (int i = age; i >= 0; --i) {
        total += sizes[i];
        if (total > survivor_capacity/2) {
            result = i;
            break;
        }
    }
}

过早晋升原因:

  • Survivor空间过小
  • MaxTenuringThreshold设置过小
  • 动态年龄判定导致过早晋升

解决方案

  1. 调整新生代大小
-XX:NewRatio=4
-XX:SurvivorRatio=8
  1. 调整对象晋升年龄
-XX:MaxTenuringThreshold=15 
  1. 关注动态年龄计算
  2. 检查是否存在大对象直接进入老年代



五、CMS Old GC频繁

问题表现

  • Old区频繁执行CMS GC
  • GC日志中出现大量"CMS Initial Mark"和"CMS Final Remark"
  • 每次回收效果不佳

底层原因



// CMS GC触发条件
void CMSCollector::shouldConcurrentCollect() {
    // 判断是否需要触发CMS GC
    if (_cmsGen->should_concurrent_collect()) {
        // 1. 根据空间占用率判断
        if (_cmsGen->occupancy() >= _bootstrap_occupancy) {
            return true;
        }
        
        // 2. 根据增长率判断
        if (isGrowingTooFast()) {
            return true;  
        }
    }
}

主要原因:

  • CMS启动阈值过高
  • 空间碎片化严重
  • 对象分配速率过快
  • 浮动垃圾过多

解决方案

  1. 调整CMS触发阈值
// 降低触发阈值,提前启动GC
-XX:CMSInitiatingOccupancyFraction=68
-XX:+UseCMSInitiatingOccupancyOnly
  1. 增大Old区空间
-XX:CMSMaxAbortablePrecleanTime=5000  
  1. 开启空间碎片整理
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0



六、单次CMS Old GC耗时长

问题表现

  • CMS GC单次停顿时间长
  • 出现较长的STW时间
  • Final Remark阶段耗时过长

底层原因



// CMS Final Remark阶段的核心代码
void CMSCollector::checkpointRootsFinalWork() {
    // 1. 处理Reference
    ReferenceProcessor::processReferences();
    
    // 2. 类卸载
    if (should_unload_classes()) {
        ClassUnloadingWork();
    }
    
    // 3. 清理符号表
    SymbolTable::clean_up();
}

耗时原因:

  • Reference处理慢
  • Class卸载耗时长
  • Symbol Table清理慢
  • 新生代状态不稳定

解决方案

  1. 优化Reference处理
// 开启并行Reference处理
-XX:+ParallelRefProcEnabled
  1. 控制Class卸载
// 关闭类卸载
-XX:-CMSClassUnloadingEnabled
  1. 稳定新生代状态
// Final Remark前强制Young GC
-XX:+CMSScavengeBeforeRemark



七、内存碎片&收集器退化



问题表现

  • CMS退化为Serial Old收集器
  • 出现"Promotion Failed"或"Concurrent Mode Failure"
  • Full GC频繁发生

底层原因



// CMS并发失败处理
void ConcurrentMarkSweepGeneration::collect_in_background() {
    if (shouldConcurrentCollect()) {
        // 并发收集失败,退化为Serial收集
        if (_foregroundGCIsActive) {
            collectSerially();
            return;
        }
        // 执行并发收集
        collectConcurrently();
    }
}

退化原因:

  • 空间碎片化严重
  • 并发模式失败
  • 晋升失败
  • 显式GC触发

解决方案

  1. 控制碎片率
// 配置碎片整理
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=10
  1. 调整并发参数
// 增加并发线程
-XX:ConcGCThreads=4
// 提前启动CMS
-XX:CMSInitiatingOccupancyFraction=68
  1. 预留足够空间
// 增大老年代空间
-Xmx4g -Xms4g




八、堆外内存OOM



问题表现

  • 进程内存超出堆内存限制
  • 出现Direct Memory OOM
  • 堆外内存增长快速

底层原因

// DirectByteBuffer分配堆外内存
class DirectByteBuffer {
    DirectByteBuffer(int cap) {
        // 通过Unsafe分配本地内存
        base = unsafe.allocateMemory(size);
        // 通过Cleaner管理内存回收
        cleaner = Cleaner.create(this, new Deallocator(base, size));
    }
}

OOM原因:

  • DirectByteBuffer使用不当
  • 堆外内存泄漏
  • JNI调用未释放
  • 本地内存过大



解决方案

  1. 限制DirectMemory大小
-XX:MaxDirectMemorySize=256M
  1. 检查DirectByteBuffer使用
  2. 排查JNI调用
  3. 开启NMT监控
-XX:NativeMemoryTracking=detail



九、JNI引发的GC问题

问题表现

  • GC Locker导致的GC
  • Young GC停顿时间变长
  • 对象过早晋升

底层原因



// GC Locker机制
class GCLocker {
    static void lock() {
        // 阻止GC发生
        _needs_gc = true;
        _jni_lock_count++;
    }
    
    static void unlock() {
        _jni_lock_count--;
        if (_jni_lock_count == 0 && _needs_gc) {
            // 触发一次GC
            Universe::heap()->collect();
        }
    }
}

问题原因:

  • JNI临界区阻塞GC
  • 触发额外的GC
  • 导致对象提前晋升

解决方案

  1. 优化JNI代码
  • 减少JNI调用时间
  • 避免在JNI中长时间持有锁
  1. 添加GC参数
// 打印JNI引起的GC信息
-XX:+PrintJNIGCStalls
  1. 调整堆内存配置
// 增大新生代空间
-XX:NewSize=256m
-XX:MaxNewSize=256m



写在最后

以上九种CMS GC问题是最常见的场景,关键是:

  1. 建立完善的监控体系
  2. 保留现场和日志信息
  3. 掌握问题分析方法
  4. 积累优化调优经验

记住一句话:

"GC优化不是目的,解决业务问题才是根本。"

#Java性能优化 #GC调优 #JVM

相关推荐

SM小分队Girls on Top,女神战队少了f(x)?

这次由SM娱乐公司在冬季即将开演的smtown里,将公司的所有女团成员集结成了一个小分队project。第一位这是全面ACE的大姐成员权宝儿(BoA),出道二十年,在日本单人销量过千万,韩国国内200...

韩国女团 aespa 首场 VR 演唱会或暗示 Quest 3 将于 10 月推出

AmazeVR宣布将在十月份举办一场现场VR音乐会,观众将佩戴MetaQuest3进行体验。韩国女团aespa于2020年11月出道,此后在日本推出了三张金唱片,在韩国推出了...

韩网热议!女团aespa成员Giselle在长腿爱豆中真的是legend

身高163的Giselle,长腿傲人,身材比例绝了...

假唱而被骂爆的女团:IVE、NewJeans、aespa上榜

在韩国,其实K-pop偶像并不被认为是真正的歌手,因为偶像们必须兼备舞蹈能力、也经常透过对嘴来完成舞台。由于科技的日渐发达,也有许多网友会利用消音软体来验证K-pop偶像到底有没有开麦唱歌,导致假唱这...

新女团Aespa登时尚大片 四个少女四种style

来源:环球网

韩国女团aespa新歌MV曝光 画面梦幻造型超美

12月20日,韩国女团aespa翻唱曲《DreamsComeTrue》MV公开,视频中,她们的造型超美!WINTER背后长出一双梦幻般的翅膀。柳智敏笑容甜美。宁艺卓皮肤白皙。GISELLE五官精致...

女网友向拳头维权,自称是萨勒芬妮的原型?某韩国女团抄袭KDA

女英雄萨勒芬妮(Seraphine)是拳头在2020年推出的第五位新英雄,在还没有正式上线时就备受lsp玩家的关注,因为她实在是太可爱了。和其他新英雄不同的是,萨勒芬妮在没上线时就被拳头当成虚拟偶像来...

人气TOP女团是?INS粉丝数见分晓;TWICE成员为何在演唱会落泪?

现在的人气TOP女团是?INS粉丝数见分晓!现在爱豆和粉丝之间的交流方法变得多种多样,但是Instagram依然是主要的交流手段。很多粉丝根据粉丝数评价偶像的人气,拥有数百、数千万粉丝的组合作为全球偶...

韩国女团MVaespa Drama MV_韩国女团穿超短裙子跳舞

WelcometoDrama.Pleasefollow4ruleswhilewatchingtheDrama.·1)Lookbackimmediatelywhenyoufe...

aespa师妹团今年将出道! SM职员亲口曝「新女团风格、人数」

记者刘宛欣/综合报导南韩造星工厂SM娱乐曾打造出东方神起、SUPERJUNIOR、少女时代、SHINee、EXO等传奇团体,近年推出的aespa、RIIZE更是双双成为新生代一线团体,深受大众与粉丝...

南韩最活跃的女团aespa,新专辑《Girls》即将发布,盘点昔日经典

女团aespa歌曲盘点,新专辑《Girls》即将发布,期待大火。明天也就是2022年的7月8号,aespa新专辑《Girls》即将发行。这是继首张专辑《Savage》之后,时隔19个月的第二张专辑,这...

章泽天女团aespa出席戛纳晚宴 宋康昊携新片亮相

搜狐娱乐讯(山今/文玄反影/图科明/视频)法国时间5月23日晚,女团aespa、宋康昊、章泽天等明星亮相戛纳晚宴。章泽天身姿优越。章泽天肩颈线优越。章泽天双臂纤细。章泽天仪态端正。女团aespa亮...

Aespa舞台暴露身高比例,宁艺卓脸大,柳智敏有“TOP”相

作为SM公司最新女团aespa,初舞台《BlackMamba》公开,在初舞台里,看得出来SM公司是下了大功夫的,虽然之前SM公司新出的女团都有很长的先导片,但是aespa显然是有“特殊待遇”。运用了...

AESPA女团成员柳智敏karina大美女

真队内速度最快最火达成队内首个且唯一两百万点赞五代男女团中输断层第一(图转自微博)...

对来学校演出的女团成员语言性骚扰?韩国这所男高的学生恶心透了

哕了……本月4日,景福男子高中相关人士称已经找到了在SNS中上传对aespa成员进行性骚扰文章的学生,并开始着手调查。2日,SM娱乐创始人李秀满的母校——景福高中迎来了建校101周年庆典活动。当天,S...