news 2026/4/18 12:53:31

技术演进中的开发沉思-329 JVM:垃圾回收(中)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
技术演进中的开发沉思-329 JVM:垃圾回收(中)

在 JVM 的内存管理体系中,垃圾收集(GC)算法就是 “回收兵法”—— 不同算法有不同的 “战术特点”,有的追求效率,有的追求无碎片,有的兼顾两者。我早年做电商库存系统时,因对算法选型一知半解,盲目使用标记 - 清除算法,导致老年代内存碎片堆积,大订单对象分配失败,触发频繁 Full GC;后来改用标记 - 整理算法,碎片问题彻底解决,系统稳定性提升一个量级。垃圾收集算法没有 “最优”,只有 “最适配”—— 新生代的复制算法、老年代的标记 - 清除 / 整理算法,以及整合它们的分代收集策略,都是 JVM 基于 “对象朝生夕死” 规律的精准选择。读懂这些算法,你就能明白 GC 的底层逻辑,也能为不同业务场景选择合适的收集器。

一、标记 - 清除算法

标记 - 清除(Mark-Sweep)是最基础、最古老的垃圾收集算法,它的核心逻辑简单到 “两步走”,却也是新手最易踩坑的算法。

1. 核心执行流程

标记 - 清除算法分为两个阶段,就像清洁工 “先标记垃圾,再清理垃圾”:

  1. 标记阶段:从 GC Roots 出发,遍历所有对象,标记出可达的存活对象;
  2. 清除阶段:遍历整个堆内存,清除所有未被标记的垃圾对象,释放内存空间。

这个算法的优点是实现简单—— 无需移动对象,只需标记和清除,早期 JVM 都采用这种算法。但它的两个致命缺点,直接决定了它只能 “屈居” 老年代:

  • 效率低:标记和清除都需要遍历整个堆,若堆内存大、对象多,两次遍历的耗时会很长,导致 STW(Stop The World)时间增加;
  • 产生内存碎片:清除垃圾对象后,释放的内存是零散的 “碎片”—— 比如堆中有 100M 空闲内存,但由多个 10M、20M 的碎片组成,此时要分配一个 50M 的大对象,明明总空闲内存足够,却因没有连续空间而分配失败,最终触发 Full GC。

2. 实战踩坑

我早年维护的电商库存系统,使用基于标记 - 清除的 CMS 收集器,运行一段时间后出现诡异现象:堆内存使用率仅 60%,但创建大订单对象(约 30M)时,频繁抛出OutOfMemoryError: Java heap space

通过jmap -heap <pid>分析堆内存,发现老年代有大量零散的内存碎片,最大的连续空闲空间只有 25M—— 这就是标记 - 清除算法的 “后遗症”。解决方法是:改用 G1 收集器(老年代用标记 - 整理算法),碎片问题立刻消失,大对象分配正常。

3. 适用场景

标记 - 清除算法适合对象存活率高的区域 —— 老年代的对象存活时间长,每次 GC 只回收少量垃圾,标记阶段的耗时远小于清除阶段,效率相对可接受。但为了避免碎片,通常会和标记 - 整理算法结合使用(比如 CMS 收集器的 “并发标记 - 清除”+“串行标记 - 整理”,定期整理碎片)。

二、复制算法

复制(Copying)算法是为解决标记 - 清除的痛点而生,它以 “牺牲部分内存” 为代价,换来了高效和无碎片的优势,也是新生代 GC 的 “标配算法”。

1. 核心执行流程

复制算法的核心思路是 “空间换时间”,它将堆内存分为大小相等的两块区域,每次只使用其中一块:

  1. 标记阶段:从 GC Roots 出发,标记出当前区域的存活对象;
  2. 复制阶段:将所有存活对象复制到另一块未使用的区域,按顺序排列,保证内存连续;
  3. 清空阶段:清空当前区域的所有对象,完成一次 GC;
  4. 交换阶段:两块区域的角色互换,下一次 GC 时使用另一块区域。

这个算法的优点非常明显:

  • 效率高:无需遍历整个堆清除垃圾,只需复制存活对象,而新生代的对象存活率极低(90% 以上的对象都是 “朝生夕死”),复制的开销很小;
  • 无内存碎片:存活对象按顺序复制到新区域,内存是连续的,大对象分配毫无压力。

但它的缺点也很突出:内存利用率低—— 只有 50% 的内存能被使用,另一块区域始终闲置,这对内存资源是极大的浪费。

2. 新生代的优化

为了解决内存利用率低的问题,JVM 对复制算法做了 “改良”—— 将新生代分为一个 Eden 区和两个大小相等的 Survivor 区(S0、S1),默认比例为Eden:S0:S1=8:1:1

改良后的执行流程(Minor GC 的核心逻辑):

  1. 新对象优先分配到 Eden 区,S0 和 S1 初始为空;
  2. Eden 区满触发 Minor GC,标记 Eden 区的存活对象,复制到空闲的 S0 区
  3. 清空 Eden 区,完成一次 GC;
  4. 下次 Minor GC 时,标记 Eden+S0 区的存活对象,复制到空闲的 S1 区
  5. 清空 Eden+S0 区,交换 S0 和 S1 的角色(S0 变空闲,S1 变使用中)。

这种设计的核心优势是:内存利用率提升至 90%(8+1 的可用区域,1 的闲置区域),既保留了复制算法高效无碎片的优点,又解决了内存浪费的问题。

3. 实战调优

新生代的SurvivorRatio参数(默认 8)决定了 Eden 和 Survivor 的比例,这个参数直接影响对象 “晋升” 到老年代的频率 —— 调大 SurvivorRatio,Eden 区变大,Minor GC 频率降低;调小 SurvivorRatio,Eden 区变小,Minor GC 频率升高。

我曾将电商系统的SurvivorRatio从 8 调整为 10(Eden:S0:S1=10:1:1),Eden 区容量增加,Minor GC 频率从每分钟 1 次降到每 3 分钟 1 次,接口响应时间减少 20%。但要注意:Survivor 区不能太小,否则存活对象无法容纳,会直接进入老年代,导致老年代内存占用飙升,触发 Major GC。

4. 适用场景

复制算法是新生代的 “专属算法”—— 新生代对象存活率低,复制开销小,90% 的内存利用率完全可接受。几乎所有新生代收集器(Parallel Scavenge、G1 的新生代收集)都采用复制算法。

三、标记 - 整理算法

标记 - 整理(Mark-Compact)算法是标记 - 清除的 “升级版”,它解决了标记 - 清除的碎片问题,也避免了复制算法的内存浪费,是老年代 GC 的 “终极方案”。

1. 核心执行流程

标记 - 整理算法分为三个阶段,兼顾了标记 - 清除的低内存消耗和复制算法的无碎片优势:

  1. 标记阶段:和标记 - 清除一样,从 GC Roots 出发,标记存活对象;
  2. 整理阶段:将所有存活对象移动到堆内存的一端,按顺序排列,保证内存连续;
  3. 清除阶段:清除堆内存另一端的所有垃圾对象,释放内存空间。

这个算法的优点是:

  • 无内存碎片:存活对象连续排列,大对象分配无忧;
  • 内存利用率高:无需额外的闲置区域,100% 的堆内存都能被使用。

缺点是:效率中等—— 整理阶段需要移动存活对象,而老年代的对象存活率高,移动开销大,导致 STW 时间比标记 - 清除长。

2. 实战价值

标记 - 整理算法是老年代的 “标配算法”——G1 收集器的老年代收集、ZGC 的整理阶段,都采用标记 - 整理算法。我曾将电商系统的 CMS 收集器换成 G1 收集器,老年代的内存碎片问题彻底解决,Full GC 频率从每小时 1 次降到每天 1 次,STW 时间从 500ms 降到 50ms 以内。

3. 适用场景

标记 - 整理算法适合对象存活率高的区域 —— 老年代的对象存活时间长,整理阶段的移动开销虽然大,但一次整理能保证长时间的无碎片内存,整体性价比更高。

四、分代收集

分代收集不是一种独立的算法,而是结合复制、标记 - 清除、标记 - 整理三种算法的 “组合策略”—— 它的核心设计逻辑是:基于对象的生命周期,将堆分为新生代和老年代,为不同代选择最适配的收集算法

1. 分代收集的核心依据

JVM 通过大量实践发现:Java 程序中的对象具有 “朝生夕死” 的特点 ——90% 以上的对象在创建后很快就会变成垃圾,只有少数对象能存活较长时间。

基于这个规律,分代收集的策略如下:

内存区域对象特点适配算法核心优势对应 GC 类型
新生代存活率低、生命周期短复制算法高效无碎片Minor GC(年轻代 GC)
老年代存活率高、生命周期长标记 - 清除 / 标记 - 整理低内存消耗、无碎片Major GC/Full GC(老年代 GC)

2. 分代收集的执行流程

分代收集的完整执行流程,就像 “清洁工分区域打扫”:

  1. 新生代 Minor GC:Eden 区满触发,采用复制算法,将存活对象复制到 Survivor 区,清空 Eden 区;
  2. 对象晋升老年代:存活对象在 Survivor 区经历多次 Minor GC 后,若仍存活(默认 15 次,可通过-XX:MaxTenuringThreshold调整),则晋升到老年代;
  3. 老年代 Major GC:老年代内存占用达到阈值触发,采用标记 - 清除 / 标记 - 整理算法,回收老年代的垃圾对象;
  4. Full GC:新生代 + 老年代的全堆 GC,通常是 Major GC 的 “升级版”,STW 时间最长,应尽量避免。

3. 实战调优

分代收集的调优核心是 “调整代际比例”,让内存分配贴合业务的对象生命周期:

  • -XX:NewRatio:新生代和老年代的比例(默认 2,即新生代:老年代 = 1:2);
    • 电商、支付等短生命周期对象多的场景:调小NewRatio(如设为 1,新生代:老年代 = 1:1),增加新生代容量,减少 Minor GC 频率;
    • 大数据、缓存等长生命周期对象多的场景:调大NewRatio(如设为 3,新生代:老年代 = 1:3),增加老年代容量,减少 Major GC 频率。
  • -XX:SurvivorRatio:Eden 和 Survivor 的比例(默认 8,即 Eden:S0:S1=8:1:1);
    • 大对象多的场景:调小SurvivorRatio(如设为 4,Eden:S0:S1=4:1:1),增加 Survivor 区容量,避免对象直接晋升老年代。

五、算法选型的适配原则

总结出垃圾收集算法的选型原则:没有最好的算法,只有最适配的场景

  1. 看对象存活率:存活率低选复制算法,存活率高选标记 - 整理算法;
  2. 看内存利用率:内存紧张选标记 - 清除 / 标记 - 整理,内存充足选复制算法;
  3. 看业务需求:低延迟场景选 G1(混合算法),高吞吐量场景选 Parallel Scavenge+Parallel Old(复制 + 标记 - 整理)。

最后小结

核心回顾

  1. 三大基础 GC 算法:标记 - 清除(简单但有碎片)、复制(高效无碎片但浪费内存)、标记 - 整理(无碎片但效率中等);
  2. 分代收集是 “组合策略”:新生代用复制算法(Minor GC),老年代用标记 - 清除 / 整理算法(Major GC),基于对象 “朝生夕死” 的规律;
  3. 实战调优核心:调整代际比例(NewRatioSurvivorRatio),让算法适配业务场景。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 5:41:59

DeepSeek-R1-Distill-Qwen-1.5B进阶使用:自定义prompt模板设计

DeepSeek-R1-Distill-Qwen-1.5B进阶使用&#xff1a;自定义prompt模板设计 你是不是也遇到过这样的情况&#xff1a;同一个问题&#xff0c;换种说法&#xff0c;模型回答质量天差地别&#xff1f;明明模型标榜“擅长数学推理和代码生成”&#xff0c;可一问复杂逻辑题&#x…

作者头像 李华
网站建设 2026/4/18 3:39:15

MinerU输出管理技巧:相对路径设置避免文件丢失

MinerU输出管理技巧&#xff1a;相对路径设置避免文件丢失 MinerU 2.5-1.2B 是一款专为复杂 PDF 文档设计的深度学习提取工具镜像&#xff0c;特别擅长处理多栏排版、嵌套表格、数学公式和高分辨率插图等传统 OCR 工具难以应对的场景。它不是简单地把 PDF 转成文字&#xff0c…

作者头像 李华
网站建设 2026/4/18 7:01:28

基于SpringBoot的服装商城销售系统(源码+lw+部署文档+讲解等)

背景及意义 基于 SpringBoot 的服装商城销售系统&#xff0c;聚焦服装零售 “交易线上化、库存一体化、运营数据化” 的核心需求&#xff0c;针对传统服装销售 “线下记账繁琐、库存对账难、客户画像模糊” 的痛点&#xff0c;构建覆盖消费者、商家、仓库管理员、运营人员的全流…

作者头像 李华
网站建设 2026/4/18 11:31:45

基于SpringBoot的演唱会门票购票网站系统(源码+lw+部署文档+讲解等)

背景及意义 基于 SpringBoot 的演唱会门票购票网站系统&#xff0c;聚焦演出票务 “购票轻量化、票源防伪化、运营数据化” 的核心需求&#xff0c;针对传统票务 “选座不直观、黄牛倒票、高并发卡顿” 的痛点&#xff0c;构建覆盖购票用户、演出主办方、平台管理员的全流程票务…

作者头像 李华
网站建设 2026/4/18 0:24:31

基于Python的养老社区的查询预约系统 计算机毕业设计选题 计算机毕设项目 前后端分离【源码-文档报告-代码讲解】

&#x1f393; 作者&#xff1a;计算机毕设小月哥 | 软件开发专家 &#x1f5a5;️ 简介&#xff1a;8年计算机软件程序开发经验。精通Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等技术栈。 &#x1f6e0;️ 专业服务 &#x1f6e0;️ 需求定制化开发源码提…

作者头像 李华
网站建设 2026/4/17 20:43:40

论文开题“救星”驾到!书匠策AI如何让你的研究赢在起点?

对于许多学术小白来说&#xff0c;论文开题就像一场“噩梦”——选题撞车、文献堆积如山、框架逻辑混乱、格式调整耗时……这些问题像一道道高墙&#xff0c;横亘在研究之路的起点。但别慌&#xff01;今天要介绍的这位“学术救星”——书匠策AI&#xff0c;正用智能科技为开题…

作者头像 李华