CMS场景YGC失败导致FULL GC的总结

引言

JVM发生频繁 CMS GC,罪魁祸首是这个参数!_-xx:+cmsscavengebeforeremark-CSDN博客https://blog.csdn.net/zl1zl2zl3/article/details/89087327 基于以上文章频繁cms gc的生产问题得出的个人总结

总结

物理分代场景下,ygc发生promotion failure都会触发full gc,但是parNew+cms组合有些非常规。在以上真实案例中,频繁发生的那些 CMS GC(每2秒一次)都没有触发 concurrent mode failure,而只有最后一次(在并发标记阶段遇到 YGC)才触发说明promotion failed 是否导致 concurrent mode failure,取决于它发生在 CMS 周期的哪个子阶段

📌 核心区分:两个不同的"失败"场景

场景 发生时间 是否导致 concurrent mode failure 你的日志中是否出现
场景 A CMS Final Remark 阶段 (由于 -XX:+CMSScavengeBeforeRemark 强制 YGC) 频繁出现 (每次 CMS GC 都有 promotion failed,但 CMS 周期继续走完)
场景 B CMS 并发标记阶段 (正常的 Allocation Failure 触发 YGC) 只出现一次(最后一次,引发 Full GC 兜底)

🔬 为什么"频繁的 CMS GC"没有触发 concurrent mode failure

你看到的现象是:每次 CMS 周期都正常执行了 Initial MarkConcurrent MarkPrecleanFinal RemarkSweepReset,尽管每次在 Final Remark 阶段都发生了 promotion failed,但 CMS 周期仍然完整结束(CMS-concurrent-sweepCMS-concurrent-reset 都完成了)。

原因在于Final Remark 阶段发生的 promotion failed 并不会让 JVM 放弃本次 CMS 周期,因为:

  1. Final Remark 是 STW 的最后一个阶段 :此时并发标记已经完成,CMS 已经知道哪些对象是垃圾。接下来的 Sweep 阶段只是清理垃圾,不需要额外的晋升空间

  2. promotion failed 只表示"本次 YGC 没成功" :由于 CMSScavengeBeforeRemark 强制执行的 YGC 本身是"可选的"(目的是减少 remark 扫描时间),即使失败了,JVM 仍然可以继续执行 remark 和 sweep。晋升失败的对象会被留在年轻代(to-space 非空),但不会破坏 CMS 周期的完整性。

  3. JVM 会"硬着头皮"完成本次 CMS 周期:因为最耗时的并发标记已经完成,放弃代价太高。JVM 选择继续,希望通过 sweep 释放一些老年代空间,缓解压力。

于是形成了死循环 :每次 CMS 周期都因为 promotion failed 没有实际回收多少老年代(甚至老年代使用量还略增),导致老年代占用率仍然 ≥80%,于是立即触发下一次 CMS 周期。如此往复,没有 concurrent mode failure,只有无尽的正常 CMS 周期。

【注:】

CMS-Final-Remark前置的YGC是简化版本,它仅能对正常年轻代进行回收。

例如第一次CMS GC,CMS-Final-Remark阶段前置的YGC执行发生了promotion failure(导致eden、survivor to非空),当第二次CMS GC,CMS-Final-Remark阶段前置的YGC发现survivor to非空就直接结束。

例如第一次CMS GC,CMS-Final-Remark阶段前置的YGC执行发生了promotion failure(导致eden、survivor to非空),也不会触发full gc。

💥 为什么最后一次触发了 concurrent mode failure

根据你的描述,最后一次 CMS GC 在并发标记阶段CMS-concurrent-mark 尚未结束时)发生了一次由 Allocation Failure 触发的 YGC。这次 YGC 也发生了 promotion failed,但这次导致了 concurrent mode failure

原因 :并发标记阶段是老年代压力最大的时期。CMS 正在标记存活对象,此时如果 YGC 需要晋升对象到老年代,而老年代没有足够的连续空间,JVM 会判定"当前 CMS 周期已经无法安全完成",因为后续的标记和清除需要依赖老年代的稳定性。于是立即抛出 concurrent mode failure,放弃当前 CMS 周期,退化为 Serial Old Full GC。

📖 回顾你的日志验证

  • 频繁 CMS GC 日志 (例如 20:05:25.466):[ParNew: 649871K->649871K(1887488K), 0.0000289 secs][GC (CMS Final Remark) 内部发生,没有 concurrent mode failure,CMS 继续走完 sweep(20:05:32.468 [CMS-concurrent-sweep: 6.221/6.233 secs])。

  • 最后一次崩溃日志 (20:14:17.847):[GC (Allocation Failure) 发生在 CMS-concurrent-mark 期间,紧接着 (concurrent mode failure),然后触发长达 3.25 秒的 Full GC。

相关推荐
南极企鹅14 分钟前
JVM-编译执行过程
jvm
苏克贝塔4 小时前
.NET开发之.net framework对比.net core
jvm
cfm_29146 小时前
JVM垃圾收集算法与收集器深度解析
jvm·测试工具·算法·性能优化
自律懒人8 小时前
AI Agent 工作流编排实战:从单 Agent 到多 Agent,手搭一套能跑通的协作系统
jvm
石一峰69910 小时前
SQLite 与 db_manager 集成关键概念详解
jvm·数据库·sqlite
布朗克1681 天前
34 JVM深入理解
java·jvm
eggrall1 天前
Linux线程:并发编程的双刃剑
jvm
程序员晨曦1 天前
深入浅出JVM内存结构
jvm·面试·职场和发展
cfm_29141 天前
JVM对象创建与内存分配机制深度解析
jvm
wuminyu1 天前
Java锁膨胀机制之偏向锁到轻量级锁源码剖析
java·linux·c语言·jvm·c++