CMS 无法与 Parallel Scavenge 配合使用,根本原因是二者分属 HotSpot 中互不兼容的分代式 GC 框架和独立吞吐量框架,缺乏协同接口与协议支持。

CMS 无法与 Parallel Scavenge 配合使用,根本原因不在参数写错或配置遗漏,而在于二者出自 HotSpot 中两套互不兼容的底层代码框架——就像用 Type-C 接口硬插 Micro-USB 插座,物理结构就不匹配。
分代式 GC 框架 vs 独立吞吐量框架
HotSpot 的垃圾收集器并非统一开发,而是分属两个“技术家族”:
- 分代式 GC 框架:Serial、ParNew、CMS、Serial Old 全部基于同一套基础架构。它们共享跨代引用处理逻辑(如卡表 Card Table 更新)、GC 触发协同机制、内存分配辅助结构。因此 ParNew 可以无缝对接 CMS,因为两者“说同一种语言”。
- Parallel Scavenge 独立框架:它从设计之初就主动脱离上述框架,由另一组开发者重写,目标明确——极致吞吐量。其年轻代(PS Scavenge)和老年代(PS MarkSweep)共用一套私有调度、内存管理与自适应调优逻辑,不依赖分代框架的接口约定。
核心冲突点:没有协同入口
当 CMS 运行时,它需要新生代收集器在 Minor GC 后主动通知晋升行为、同步记忆集(Remembered Set)状态、协调并发标记阶段的老年代快照。但 Parallel Scavenge 不提供这些回调钩子,也不实现分代框架定义的 GenCollectedHeap::collect() 标准协作协议。
结果是 JVM 在启动时直接拒绝组合:
不是算法不兼容,而是“没机会对话”
有人误以为是“复制算法 vs 标记-清除”导致冲突——其实不然。CMS 的老年代用标记-清除,ParNew 的年轻代也用复制;Parallel Scavenge 同样用复制。算法层面并无本质矛盾。
真正卡死的是架构层:
- CMS 要求年轻代收集器参与并发周期的“初始标记”“重新标记”等 STW 阶段协同,Parallel Scavenge 完全不响应这类事件。
- Parallel Scavenge 强依赖
UseAdaptiveSizePolicy动态调优 Eden/Survivor 比例,而 CMS 的晋升模型与此策略不兼容,容易引发频繁 Full GC 或 Survivor 溢出。 - 源码中明确禁止:
if (UseParallelGC && UseConcMarkSweepGC) vm_exit_during_initialization(...)
正确替代路径
根据实际目标选择已有验证的组合:
-
低延迟场景:用
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC(注意 JDK 9+ 已移除 CMS) -
高吞吐量场景:用
-XX:+UseParallelGC -XX:+UseParallelOldGC,二者同源,支持自适应堆调优与并行老年代整理 -
现代替代方案:JDK 11+ 推荐 G1(
-XX:+UseG1GC),兼顾延迟与吞吐,无需手动搭配新生代/老年代收集器


















