serial收集器本质是单线程+安全暂停+代际适配,stw是保障内存一致性的必要手段:新生代用复制算法(eden/survivor流转),老年代由serial old用标记-整理算法回收,全程需暂停用户线程以确保引用静止与内存布局稳定。

Serial收集器的工作流程本质是“单线程 + 安全暂停 + 代际适配”,它的STW不是设计缺陷,而是保证内存一致性的必要手段。
新生代回收:Eden与Survivor的复制流转
当Eden区填满触发Young GC时,Serial收集器执行以下动作:
- 立即进入STW状态,所有用户线程在安全点(Safe Point)暂停,不再修改堆中对象引用;
- 单线程扫描Eden区和当前From Survivor区,标记存活对象;
- 将存活对象复制到To Survivor区——若对象年龄达到-XX:MaxTenuringThreshold设定值,或To区空间不足,则直接晋升至老年代;
- 清空Eden和From区,再交换From与To角色,为下次GC准备就绪。
整个过程不整理碎片,靠复制天然获得连续内存,契合新生代对象“朝生夕灭”的特征。
老年代回收:Serial Old的标记-整理三步法
当发生Full GC(如老年代空间不足、Minor GC晋升失败等),Serial Old接管老年代回收:
- 同样先STW,冻结所有应用线程;
- 从GC Roots出发,递归标记所有可达对象;
- 将所有存活对象向堆起始端紧凑移动,消除中间空隙;
- 更新所有指向这些对象的引用地址,并清理移动后边界外的内存。
这种整理方式避免了碎片堆积,也比复制算法更节省资源——毕竟老年代对象存活率高,复制开销太大。
为什么必须STW?两个根本约束
STW不是为了“方便”,而是由垃圾回收的底层逻辑决定的:
- 引用关系必须静止:GC需要准确识别哪些对象存活。如果用户线程边运行边修改引用(比如把某个对象赋给新变量、断开旧引用),标记结果就会错漏——该活的被回收,该死的却保留;
- 内存布局不能动态变化:复制或整理阶段要移动对象、更新指针。若此时其他线程正在读写这些地址,轻则读到脏数据,重则引发JVM崩溃。
Serial用最简方式满足这两个约束:只让一条GC线程干活,其余全部停摆。它不追求并发,而追求确定性。
单线程≠低效,而是取舍后的合理选择
在小堆(几十MB)、单核CPU或嵌入式场景中,Serial反而有优势:
- 没有线程创建、同步、负载均衡等开销;
- 缓存局部性好,指令流水线稳定;
- 几十毫秒的STW停顿对桌面应用或后台工具影响有限。
它的价值不在“快”,而在“稳”和“省”——适合资源受限但不需要高吞吐、低延迟的环境。











