serial收集器是jvm中最基础的单线程垃圾收集器,新生代采用标记-复制算法,老年代搭配serial old使用标记-整理算法,仅适用于资源受限、延迟不敏感的轻量级场景,如嵌入式设备、命令行工具和教学演示。

Serial收集器是JVM中最基础的垃圾收集器,它的核心不是“慢”,而是“单线程 + 全局停顿”的确定性行为。理解它,关键在于看清它怎么干活、为什么只适合特定场景,而不是泛泛而谈“古老”或“简单”。
新生代用复制算法,一次GC只扫Eden和一个Survivor
Serial在新生代采用标记-复制(Mark-Copy):每次GC只扫描Eden区和当前From Survivor区中的存活对象,把它们一次性复制到To Survivor区;复制过程中,年龄达标或To空间不足的对象直接晋升老年代;复制完成后清空Eden和From,再交换From/To角色。整个过程不整理碎片、不跨区扫描,快且干净——但前提是对象少、堆小。
- 它不扫描整个堆,只聚焦活跃区域,所以几十MB堆里一次Minor GC常在10–50ms内完成
- 没有并发标记、没有写屏障、没有跨代引用处理,逻辑极简,启动和执行开销极低
- 但一旦Eden填满频率升高(比如每秒多次GC),STW就会密集打断应用线程,响应抖动明显
老年代用标记-整理,移动所有存活对象来避免碎片
Serial Old负责老年代回收,走的是标记-整理(Mark-Compact)三步:先从GC Roots出发标记所有存活对象;再将它们向内存起始端紧凑迁移;最后修正所有引用并清理尾部空间。这比标记-清除更稳妥,能保证后续大对象分配不因碎片失败——但代价是必须暂停全部线程,且移动成本随存活对象数量线性增长。
- 对128MB以内老年代,一次Full GC可能控制在100–200ms;超过256MB,很容易突破300ms
- 它不做并发标记,也不分阶段,整个流程串在一条线程上,无法拆解或并行加速
- 容器环境里即使分配了4核,它也只用1个逻辑核,其余CPU全程闲置
单线程不是缺陷,而是设计取舍:省开销换确定性
Serial不追求吞吐或延迟指标,它追求的是最小资源占用下的可预测行为。没有线程创建、无同步锁、无跨线程通信——这些在多核大堆里是短板,在嵌入式或CLI工具里反而是优势。
- 它适合生命周期短、内存用量低、无实时交互需求的程序,比如Gradle插件、配置校验脚本、IoT设备上的Java代理
- 教学调试时启用-XX:+UseSerialGC,GC日志干净无干扰,便于观察对象晋升、年龄阈值等机制
- 但它绝不该出现在Spring Boot Web服务、微服务API或任何依赖稳定响应的系统中——哪怕堆只有256MB
不能混用、不可替代:启用方式与误用风险
必须显式加-XX:+UseSerialGC才能启用,现代JDK(9+)默认不用它。这个参数会同时激活Serial(新生代)和Serial Old(老年代),且与其他GC参数互斥。
- 若同时写了-XX:+UseSerialGC和-XX:+UseG1GC,以命令行中后出现的为准
- 在Docker容器中配了-XX:+UseSerialGC却给了2核以上,CPU利用率长期低于30%,其实是GC卡住了其他核
- 压测QPS上不去、监控显示“GC频繁但CPU不高”,大概率就是Serial在单线程死扛,业务线程全在等它










