Serial收集器是HotSpot中最基础的单线程GC,年轻代用DefNewGeneration+Copying算法,老年代用TenuredGeneration+MarkSweepCompact算法,全程Stop-The-World且无并发设施。

Serial收集器是HotSpot虚拟机中最基础、最简单的垃圾收集器,采用单线程执行所有GC工作(包括Minor GC和Full GC),适用于客户端模式或小型应用。它的实现逻辑简洁直接,核心在于“Stop-The-World”下的串行标记-复制(Young)与标记-整理(Old)流程,全部由一个JVM线程完成,无并发协调开销。
年轻代回收:DefNewGeneration + CopyTask
Serial收集器的年轻代使用DefNewGeneration类实现,基于Copying算法(半区复制)。触发Minor GC时,JVM调用DefNewGeneration::collect(),主要步骤如下:
- 暂停所有Java线程(安全点同步完成)
- 扫描GC Roots(栈帧局部变量、静态字段、JNI引用等),标记存活对象
- 将Eden区和From Survivor中存活对象复制到To Survivor区;若对象年龄达标或To区空间不足,则晋升至老年代
- 交换From/To指针,清空Eden与原From区
关键细节:复制过程由CopyTask封装,但实际在单线程中顺序执行,不启动额外任务队列;对象晋升通过TenuredGeneration::promote()委托给老年代处理。
老年代回收:TenuredGeneration + MarkSweepCompact
Serial的老年代使用TenuredGeneration,配合MarkSweepCompact算法(即“标记-清除-整理”)。Full GC时调用TenuredGeneration::collect(),流程为:
- 以年轻代GC后存活对象 + 老年代自身GC Roots为起点,进行完整标记
- 清除所有未被标记的对象(内存释放)
- 将剩余存活对象向内存起始端紧凑排列,消除碎片——这是Serial区别于CMS的关键特征
整理阶段由CompactibleSpace::compact()驱动,按对象大小顺序移动,并更新所有指向这些对象的引用(包括OopMap、栈帧、常量池等),该过程完全串行且需遍历整个老年代。
GC触发与线程协作机制
Serial不依赖后台GC线程,所有操作由应用线程(即发起GC的Java线程)直接执行。其触发路径典型为:
- Eden区分配失败 → 触发GenCollectedHeap::do_collection()
- 判断是否为Full GC(如晋升失败、System.gc()显式调用、元空间OOM后兜底等)
- 根据_collector_policy选择对应代的collect方法(DefNew + Tenured组合)
- 全程持有Universe::heap()->gc_cause()上下文,记录GC原因并统计时间
值得注意的是,Serial没有写屏障(Write Barrier)、卡表(Card Table)或并发标记位图等复杂设施,所有对象图遍历均基于原始Oop结构+句柄访问,因此代码路径短、可读性强,适合理解JVM GC主干逻辑。
源码定位与关键类关系
在OpenJDK 8u源码中,Serial相关实现集中在src/hotspot/share/gc/serial/目录:
- defNewGeneration.hpp/cpp:定义年轻代行为,含copy_to_survivor_space等核心方法
- tenuredGeneration.hpp/cpp:提供老年代空间管理与mark-sweep-compact入口
- serialCollectorPolicy.hpp:决定新生代/老年代比例、初始大小等策略参数
- genCollectedHeap.hpp/cpp:串联两代,实现统一collect接口
调试时可重点关注DefNewGeneration::collect()和TenuredGeneration::collect()的调用栈,结合-XX:+PrintGCDetails -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly辅助观察行为。

















