parallel gc 的设计原点是最大化吞吐量而非减少停顿,通过多线程并行执行stw阶段任务压缩单次gc耗时,从而提升用户线程运行时间占比;其所有机制(如禁用并发标记、依赖-xx:gctimeratio硬约束)均围绕“多跑”目标展开。

Parallel GC 的吞吐量目标不是副作用,而是设计原点
它从第一行代码就不是为“少停”写的,而是为“多跑”写的。吞吐量 = 用户线程执行时间 / (用户线程执行时间 + GC 时间),Parallel GC 所有机制都围绕放大分子、压缩分母展开——比如用多个线程并行做标记和复制,把一次 Minor GC 从 100ms 压到 30ms,哪怕仍要 STW,整体单位时间里用户代码跑得更多了。
为什么不用并发标记来进一步减少停顿
因为并发标记要维护 Remembered Set、处理跨区引用、协调应用线程写屏障,这些额外开销会吃掉 CPU 时间,反而拉低吞吐。Parallel GC 直接放弃这部分“软实时”能力,换回确定的并行效率:所有 GC 线程全力干活,不等应用线程,也不让应用线程等它。实测中,在 4–8GB 堆、16 核 CPU 的批处理任务上,UseParallelGC 比 UseG1GC 的总 GC 时间通常低 15%–25%,尤其在对象分配速率稳定时优势更明显。
参数 -XX:GCTimeRatio 是吞吐量的硬约束,不是建议值
-
-XX:GCTimeRatio=99表示 JVM 必须让 GC 时间 ≤ 总时间的 1%,它会主动扩大堆、调高晋升年龄、放宽 Survivor 区使用率,甚至推迟 Full GC,只为守住这个比例 - 设成
19(即允许 5% GC 时间)不是“放松”,而是告诉 JVM:“你可以更激进地压缩年轻代,换更少的 GC 次数”——这在日终对账类任务中常比默认值更稳 - 而
-XX:MaxGCPauseMillis在 Parallel GC 中是软目标,设太低(如 50)会导致 JVM 频繁触发小规模回收,实际 GC 总耗时反而上升,吞吐崩盘
容易被忽略的硬件适配细节
Parallel GC 的线程数不是越多越好:-XX:ParallelGCThreads 默认按公式计算(≤8 核时等于核数),但真实生产中,如果应用本身也是 CPU 密集型(比如 Spark task 或矩阵运算),应设为物理核心数的 70%~80%,留出资源给业务线程调度和 OS 中断处理;否则 GC 线程抢光 CPU,应用线程频繁被 preempt,STW 时间没变短,整体吞吐反而下降。











