串行垃圾回收器
-XX:+UseSerialGC = Serial + SerialOld
-XX:+UseSerialGC
代表使用 Serial + SerialOld 的组合:
Serial → 负责新生代(Young Generation)垃圾回收
SerialOld → 负责老年代(Old Generation)垃圾回收
它是 最基础的垃圾回收器,所有操作都是单线程执行的。
执行流程
当 Serial GC 启动时:
所有用户线程(蓝线)都会在安全点被 暂停(阻塞);
仅有一个 GC 线程(红线)在工作,执行回收任务。
在此期间,程序完全停止,只有垃圾回收在运行。
这也是**“串行(Serial)”**名称的由来——只有一个线程负责 GC 操作。
垃圾回收算法
新生代:使用 复制算法(Copying)
存活对象从 Eden + SurvivorFrom → SurvivorTo;
清空 Eden 区;
老年代:使用 标记-整理算法(Mark-Compact)
标记存活对象;
整理内存以减少碎片。
吞吐量优先
Parallel GC
-XX:+UseParallelGC
-XX:+UseParallelOldGC
这表示:
新生代(Young Generation) 使用 Parallel Scavenge GC;
老年代(Old Generation) 使用 Parallel Old GC
-XX:GCTimeRatio-n -> GC时间占总时间比例 1 / 1 + ratio
和 Serial GC 最大的不同在于:
在安全点(Safe Point)之后,Parallel GC 会同时启动多个垃圾回收线程(红线)。
执行流程
应用运行阶段(蓝线) 多线程并行执行业务逻辑。
进入安全点(Safe Point) JVM 触发 GC,暂停所有应用线程(Stop-The-World)。
并行回收阶段(红线) 多个 GC 线程并行执行标记、复制、清理操作。
新生代采用 复制算法(Copying);
老年代采用 标记-整理算法(Mark-Compact)。
回收完成 → 应用恢复(蓝线) 所有垃圾回收线程结束,应用线程恢复执行。
响应时间优先
CMS
-XX:+UseConcMarkSweepGC
这会启用:
新生代:ParNew(并行复制算法)
老年代:CMS(并发标记清除算法)
如果 CMS 失败,则回退使用 SerialOld。
💡 CMS 的目标:尽量让 GC 与应用线程同时执行,减少长时间停顿。
⚠️CMS 的主要问题
🧱 1️⃣ 内存碎片问题(最致命问题)
CMS 使用 标记-清除算法(Mark-Sweep) :
- 它只清除“死亡对象”,不整理内存空间。
- 结果是——产生大量的 内存碎片。
📉 影响:
-
大对象(需要连续内存空间)可能无法分配;
-
虽然老年代还有很多空闲内存,但分配失败会触发:
Concurrent Mode Failure然后 JVM 被迫使用 Serial Old GC(单线程、Stop-The-World) 进行整理,造成严重的长时间停顿。
💬 比喻:
CMS 就像清理房间时只是“把垃圾扫走”,但没整理地毯、没叠被子,结果空间虽大但不连贯。
🕳️ 2️⃣ “Concurrent Mode Failure” 并发模式失败
这个错误非常典型,说明:
CMS 在并发清理的过程中,应用线程仍在分配对象,结果老年代很快被填满,GC 还没清完垃圾就被逼停下。
于是:
- JVM 触发 Full GC(Serial Old) ;
- 所有线程暂停;
- 延迟陡增。
📘 解决方向:
-
提前触发 CMS:
-XX:CMSInitiatingOccupancyFraction=70 -
保留足够的空闲空间让应用分配;
-
或者直接改用 G1 GC。
⚙️ 3️⃣ “浮动垃圾(Floating Garbage)” 问题
CMS 的标记与清除是并发执行的。
在并发标记阶段,应用线程仍在创建和销毁对象。
因此:
- 有些在标记阶段创建、并在清理前销毁的对象无法被识别为垃圾;
- 它们会暂时留到下一次 GC 才能清除;
- 这部分就是所谓的 “浮动垃圾” 。
📉 影响:
- 降低内存利用率;
- 加大下一次 GC 的压力。
🧮 4️⃣ CPU 资源竞争(并发阶段消耗性能)
CMS 的并发阶段与应用线程同时执行。
这意味着:
- GC 线程和业务线程 同时争夺 CPU 资源;
- 如果 CPU 核心数不多,应用性能可能下降;
- 延迟虽然低,但整体吞吐量下降。
📘 举例:
在 4 核机器上,如果 CMS 开启 2 个 GC 并发线程,那么业务线程可用的 CPU 就只剩一半。
📊 5️⃣ GC 周期复杂、调优困难
CMS 的触发时机和线程配置都需要手动调优,参数多且相互影响:
-
触发比例:
-XX:CMSInitiatingOccupancyFraction=70 -
并发线程数:
-XX:ConcGCThreads -
重新标记优化:
-XX:+CMSScavengeBeforeRemark
若配置不当:
- 可能太早触发 GC,浪费资源;
- 也可能太晚,导致 Concurrent Mode Failure;
- 难以预测停顿时间。
🧯 6️⃣ 无法处理巨堆(大内存)场景
CMS 的并发标记和清理算法在大堆(>8GB)场景下效率会明显下降:
- 标记范围太大;
- 线程切换成本上升;
- 对延迟和吞吐量的平衡越来越难控制。
因此,从 JDK 9 开始默认启用 G1;
从 JDK 14 起,CMS 已被正式标记为废弃(Deprecated) 。
🧠 三、CMS 的问题总结表
| 问题 | 描述 | 影响 | 解决方案 |
|---|---|---|---|
| 内存碎片 | 标记-清除不整理内存 | 分配失败、Full GC | 换用 G1 / 调整堆 |
| 并发模式失败 | GC 还没清完就满了 | 触发 Full GC | 提前触发 CMS |
| 浮动垃圾 | 并发标记期间产生的新垃圾无法清理 | 占用内存 | 下次 GC 再清 |
| CPU 竞争 | GC 线程与业务线程抢资源 | 吞吐量下降 | 增加 CPU 核数 |
| 调优复杂 | 参数多、互相影响 | 配置困难 | 改用自适应的 G1 |
| 不适合大堆 | 标记耗时、碎片多 | 延迟大、效率低 | 使用 G1/ZGC |
G1垃圾回收器
图中有三个三角形节点,代表 G1 的三种主要垃圾回收阶段:
- 🟢 Young Collection(年轻代收集)
- 🔵 Young Collection + Concurrent Mark(年轻代收集 + 并发标记)
- 🟠 Mixed Collection(混合收集)
这三者形成一个循环流程:
Young → Young + Concurrent Mark → Mixed → 回到 Young
三个阶段的含义与关系
🟢 1️⃣ Young Collection(年轻代收集)
- 对象首先分配在 Eden 区域。
- 当 Eden 填满时触发 Young GC。
- 使用 复制算法(Copying) :
Eden → Survivor / Old。 - 停顿类型:Stop-The-World(STW) 。
- 通常是 并行执行,多个 GC 线程同时处理。
📘 特点:
- 只处理年轻代的 Region;
- 不涉及老年代;
- 发生频率高,但停顿短。
🔵 2️⃣ Young Collection + Concurrent Mark(年轻代收集 + 并发标记)
当 JVM 监测到老年代使用率超过阈值(通常是 45%)时,
在一次 Young GC 之后,会 启动全堆的并发标记(Concurrent Marking) 阶段。
这个阶段包含两个过程:
- 一次普通的 Young GC(STW) ;
- 紧接着开始 并发标记(与应用线程并发执行)。
💡 并发标记的目标:
- 扫描整个堆中所有 Region;
- 标记哪些对象是存活的;
- 统计每个 Region 的“垃圾比例”。
🧭 这样,G1 就能知道——哪些 Region 的“垃圾最多”,
为后面的 Mixed Collection 做准备。
🟠 3️⃣ Mixed Collection(混合收集)
标记阶段完成后,G1 会执行一系列 Mixed GC:
- 同时回收年轻代和部分老年代的 Region(即“混合”)。
- 优先清理垃圾比例高的老年代 Region(“Garbage First” 的由来)。
- 每次回收的 Region 数量有限(由暂停时间目标决定)。
- 依旧是 STW + 并行回收。
📘 Mixed GC 会重复多次,直到老年代回收足够多空间。
之后,回收循环重新开始(回到 Young Collection 阶段)。