JVM(9)——详解Serial垃圾回收器

Serial 垃圾回收器是 JVM 最古老、最基础、最简单的垃圾回收器,也是理解其他更复杂回收器的基础。

一、Serial 回收器的定位与设计目标

  1. 核心特点:单线程 (Single-Threaded)

    • 这是 Serial 回收器最根本的特征。无论是进行 垃圾标记 (Marking)清除 (Sweeping)复制 (Copying) 还是 整理 (Compacting) ,它都只使用一个单独的线程来执行所有垃圾回收工作。
  2. 工作模式:Stop-The-World (STW)

    • 在进行垃圾回收时,它必须暂停所有应用线程 (Application Threads)。这意味着在 GC 期间,整个 Java 应用是完全停止响应的。
  3. 设计目标:简单性与客户端/低资源环境

    • 简单性: 算法实现简单直接,代码量小,易于理解和维护。它是其他复杂回收器的基础。

    • 低内存/CPU 开销: 单线程工作意味着它自身消耗的内存(元数据)和 CPU 资源非常少。

    • 客户端模式 (Client Mode) 默认: 在 JDK 8 及之前的版本中,对于运行在 客户端模式 (Client Mode) 下的 JVM(通常指桌面 GUI 应用或小型应用),Serial 回收器是年轻代的默认回收器(老年代默认搭配 Serial Old)。这是因为桌面应用通常对短暂的停顿不那么敏感,且更看重 JVM 启动速度和资源占用。

  4. 适用场景:

    • 运行在资源极其受限的环境(如嵌入式设备、微控制器)。

    • 单核 CPU 环境(多线程 GC 在此类环境没有优势)。

    • 小型堆内存(几十 MB 到一两百 MB)。

    • 短暂停顿不敏感的应用(如简单的命令行工具、后台守护进程)。

    • 作为学习 JVM GC 原理的基础模型

二、核心组件与算法

Serial 回收器通常指代两个具体的回收器实现,分别管理不同的代:

  1. 年轻代:Serial 回收器 (Serial Collector)

    • 算法:复制算法 (Copying Algorithm)

    • 堆结构: 年轻代划分为一个较大的 Eden 区和两个较小的 Survivor 区 (From, To)。默认比例 Eden : Survivor = 8:1

    • 回收过程 (STW):

      1. 触发条件: Eden 区空间不足以分配新对象。

      2. STW: 暂停所有应用线程。

      3. 标记: 单 GC 线程从 GC Roots (栈引用、静态变量、JNI 引用等)开始,标记 Eden 区和当前 From Survivor 区中的存活对象

      4. 复制: 单 GC 线程将标记出的存活对象复制To Survivor 区。

        • 新对象直接在 Eden 区分配。

        • 对象在 Survivor 区之间每熬过一次 GC,年龄增加 1。

        • 达到晋升年龄阈值 (-XX:MaxTenuringThreshold) 的对象会被晋升 (Promote) 到老年代。

        • 如果 To Survivor 区空间不足以容纳所有存活对象,或者存活对象年龄过大,会直接晋升到老年代。

      5. 清空与交换: 单 GC 线程清空 Eden 区和刚使用完的 From Survivor 区。交换 FromTo 的角色(下次 GC 时,当前的 To 变成新的 From)。

    • 特点: 算法简单高效,无内存碎片。STW 时间与存活对象数量成正比。对于小型堆和存活对象少的应用,停顿时间可以很短(几十毫秒)。

  2. 老年代:Serial Old 回收器 (Serial Old Collector)

    • 算法:标记-整理算法 (Mark-Sweep-Compact Algorithm)

    • 回收过程 (STW):

      1. 触发条件:

        • 显式调用 System.gc()

        • 老年代空间不足(年轻代对象晋升失败,或大对象直接分配失败)。

        • 元空间 (Metaspace) / 永久代 (PermGen) 空间不足。

        • 在某些情况下,作为 CMS 或 G1 回收失败时的后备方案 (Promotion FailedConcurrent Mode Failure 时触发的 Full GC)。

      2. STW: 暂停所有应用线程。

      3. 标记: 单 GC 线程从 GC Roots 出发,递归遍历整个老年代对象图,标记所有存活对象

      4. 整理 (滑动压缩):

        • 计算位置: 单 GC 线程计算每个存活对象在整理后应该移动到的目标地址(通常是堆内存空间的起始端开始紧密排列)。

        • 更新引用: 单 GC 线程更新所有指向被移动对象的引用(指针),确保应用恢复后能找到正确的新位置。这一步需要遍历整个对象图,非常耗时。

        • 移动对象: 单 GC 线程将存活对象复制(移动)到计算好的新地址。

      5. 清除: 单 GC 线程回收掉整理后边界以外的所有空间(即死亡对象占用的空间)。

    • 特点:

      • 解决内存碎片: 整理后得到连续的大块空闲空间,消除了内存碎片。

      • STW 时间长: 整个标记-整理-更新引用过程由单线程 完成,对于大堆存活对象多 的老年代,停顿时间会非常长(几秒甚至几十秒),对应用响应性影响巨大。

      • 可靠性高: 作为最简单、最"笨重"的回收器,它通常是其他回收器失败时的最后保障。

三、核心特性与优劣势

  • 优势:

    1. 极低的内存开销: 自身数据结构简单,几乎不占用额外的堆内存(不像 G1 需要 RSet,CMS 需要卡表等)。

    2. 极低的 CPU 开销 (GC 线程本身): 仅使用一个 GC 线程,在非 GC 期间没有额外线程消耗 CPU。

    3. 算法简单可靠: 实现简单,逻辑清晰,是学习 GC 原理的绝佳起点。作为后备方案非常稳定。

    4. 无内存碎片 (Serial Old 整理后): 老年代回收后空间连续。

    5. 适合微型/嵌入式环境: 在资源极其受限(内存小、CPU 单核)的场景下是唯一或最佳选择。

  • 劣势:

    1. 长时间的 STW 停顿: 这是最大的缺点!单线程 处理整个堆(尤其是老年代)的垃圾回收工作,导致 GC 停顿时间与堆大小、存活对象数量成正比。对于现代动辄几 GB 甚至更大的堆,Serial Old 的 Full GC 停顿时间完全不可接受。

    2. 完全暂停应用: 在 GC 期间,应用完全停止服务,无法响应任何请求。这对需要交互性或低延迟的应用(Web 服务、GUI 应用)是致命的。

    3. 无法利用多核优势: 在现代多核 CPU 成为标配的情况下,Serial 回收器无法并行化利用多个核心来加速 GC 过程,造成了巨大的硬件资源浪费。

    4. 吞吐量相对较低: 虽然 GC 自身开销小,但长时间的 STW 直接减少了应用运行的时间,整体吞吐量通常低于 Parallel 或 G1 等多线程回收器(在堆较大时尤其明显)。

四、关键配置参数

  • 启用 Serial 回收器 (JDK 8 及之前 Client 模式默认):

    • 年轻代: -XX:+UseSerialGC (显式指定)

    • 老年代: Serial Old 会自动搭配启用。

  • JDK 9+ 的变化:

    • 客户端/服务器模式区分逐渐淡化。G1 成为所有平台的默认回收器。

    • 但 Serial 和 Serial Old 仍然存在,可通过 -XX:+UseSerialGC 显式启用。

  • 其他相关参数 (通常使用默认值即可):

    • -XX:SurvivorRatio: Eden 与 Survivor 区的比例 (默认 8)。

    • -XX:MaxTenuringThreshold: 对象晋升老年代的最大年龄 (默认 15)。

    • -Xmn: 设置年轻代大小 (覆盖 -XX:NewRatio)。

    • -XX:NewRatio: 老年代与年轻代的比例 (默认在 Client 模式下为 2,即 老年代:年轻代=2:1)。

五、使用场景总结

  1. 历史/默认场景: JDK 8 及之前客户端模式 (Client Mode) 下年轻代的默认回收器。

  2. 资源极端受限环境: 嵌入式系统、IoT 设备、功能手机等内存 (RAM) 极小 (几十MB) 且 CPU 为单核的环境。

  3. 简单/非交互式应用: 命令行工具、一次性批处理脚本、后台守护进程等对短暂停顿完全不敏感的小型应用。

  4. 后备方案: 当其他更先进的回收器 (如 CMS, G1) 发生失败 (Concurrent Mode Failure, Promotion Failed) 时,JVM 会退化使用 Serial Old 进行兜底的 Full GC。

六、与其它回收器的对比

特性 Serial (Young) / Serial Old (Old) Parallel Scavenge (Young) / Parallel Old (Old) CMS (Old) G1
线程模型 单线程 多线程 (并行) 多线程 (并发 + 并行) 多线程 (并行 + 并发)
STW 完全 STW 完全 STW 部分 STW (初始标记、重新标记) 部分 STW (Young GC, Mixed GC 阶段)
目标 简单、低开销、小堆、Client 模式 高吞吐量 低延迟 (老年代) 可预测低停顿、高吞吐、大堆
算法 复制 (Young) / 标记-整理 (Old) 复制 (Young) / 标记-整理 (Old) 标记-清除 (Old) 复制 (Region 间)
碎片 无 (Old 整理后) 无 (Old 整理后) 有 (需 Full GC 整理)
适用堆 极小堆 (MB 级) 中小型 -> 大型堆 中小型堆 (GB 级) 大堆 (GB -> TB 级)
适用场景 嵌入式、Client 应用、后备 后台计算、批处理 Web 服务 (历史方案) 主流服务器应用 (现代默认)
JDK 默认 Client Mode (<=JDK8) Server Mode (<=JDK8) >= JDK9
CPU 利用 低 (单核) 高 (并行) 中高 (并发+并行,争抢 CPU) 高 (并行+并发)

七、总结

Serial 垃圾回收器是 JVM GC 的基石和起点。它的核心价值在于:

  1. 极致的简单性: 单线程 STW 模型,算法清晰易懂。

  2. 最小的资源开销: 内存和 CPU 占用极低。

  3. 微/嵌入式场景的适用性: 在资源受限环境中不可或缺。

  4. 可靠的后备: 作为其他先进回收器失败时的安全网。

然而,其单线程完全 STW 的本质,使得它在面对现代多核处理器和大内存堆的应用需求时,停顿时间过长 的缺点暴露无遗,完全不适合需要低延迟或高吞吐的服务器端应用

相关推荐
蚰蜒螟1 分钟前
Spring与SLF4J/Logback日志框架深度解析:从源码看日志系统设计
java·spring·logback
IT_10243 分钟前
springboot企业级项目开发之项目测试——集成测试!
spring boot·后端·spring·集成测试
还债大湿兄12 分钟前
Qt蓝图式技能编辑器状态机模块设计与实现
开发语言·qt
Dxy123931021632 分钟前
python如何做实时资讯分析
开发语言·python
想用offer打牌33 分钟前
一站式了解RocketMQ如何实现顺序消息😵
后端·rocketmq
不吃肉的羊35 分钟前
Apache开启gzip压缩
后端
明天不吃。38 分钟前
【数据结构】七种常见排序算法
java·数据结构·排序算法
崔小汤呀1 小时前
java8-java17新特性总结
java
Edingbrugh.南空1 小时前
Kafka性能调优全攻略:从JVM参数到系统优化
jvm·分布式·kafka
喵手1 小时前
如何高效进行对象拷贝?浅拷贝与深拷贝的陷阱,你知道吗?
java·后端·java ee