两个门派
GC 的世界有一场持续至今的分家:吞吐量优先关注总账——单位时间里干完的活最多,单次停顿长点无所谓;延迟优先关注体验——每次停顿都要短,哪怕总开销更大。批处理跑数、离线计算选前者;在线交易、面向用户的服务选后者。所有收集器都可以放进这张坐标系里看。
新生代三件套
Serial:单线程收集,收集时全停顿——简单高效没废话,客户端和小内存场景至今在用;ParNew:Serial 的多线程版,为配合 CMS 而生;Parallel Scavenge:也是多线程,但目标是可控的吞吐量,提供 -XX:MaxGCPauseMillis 和 -XX:GCTimeRatio 两个旋钮,还支持自适应调节(GC 自动向代尺寸要空间)。JDK 8 的默认组合是 Parallel Scavenge + Parallel Old——服务端吞吐优先的务实选择。
CMS:并发收集的第一位选手
// CMS 老年代回收四阶段
// 1. 初始标记 STW,极短:只标记 GC Roots 直接关联的对象
// 2. 并发标记 与用户线程并行:沿着引用链摸完整堆(最耗时但不停顿)
// 3. 重新标记 STW,较短:修正并发期间用户线程改动的部分
// 4. 并发清除 与用户线程并行:把死对象就地清掉(标记-清除算法)
//
// 精髓:把最耗时的活儿挪到并发阶段,STW 只剩两个短环节
// 代价:并发=和业务抢 CPU;清除=留下碎片CMS 的三宗罪
一,CPU 敏感:并发阶段默认占(核数+3)/4 的线程,核少时业务明显变慢。二,浮动垃圾:并发标记和清除期间产生的新垃圾只能等下轮,如果老年代增长太快、清理前就满了,并发模式失败(Concurrent Mode Failure),JVM 会启动保命方案——Serial Old 单线程整理整堆,一次超长 STW,比不用 CMS 还惨。缓解办法是把 -XX:CMSInitiatingOccupancyFraction(默认 92%)调低、提前触发,但调太低又浪费 GC 次数。三,碎片:标记-清除不给对象搬家,碎片积累到分配不出连续空间,只能靠 Full GC 前的压缩救场。这三宗罪注定了 CMS 的退场——JDK 9 被标记废弃,JDK 14 彻底移除。
经典组合速查
| 新生代 | 老年代 | 定位 |
|---|---|---|
| Serial | Serial Old | 单线程,客户端/小内存 |
| ParNew | CMS + Serial Old 兜底 | 延迟优先(JDK 8 时代主流) |
| Parallel Scavenge | Parallel Old | 吞吐优先,JDK 8 默认 |
CMS 教会业界一件事:停顿可以并发化,但"边干活边搬家"在当时做不到——所以它只能选清除,只能留碎片。把并发化和"无碎片"同时做到的,是下一篇的主角 G1。
评论 (0)