news 2026/9/7 3:47:43

JVM垃圾回收机制详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JVM垃圾回收机制详解

JVM垃圾回收机制详解

1. 引言

1.1 什么是垃圾回收机制?

垃圾回收(Garbage Collection,GC)是JVM自动管理内存的一种机制。它负责回收不再使用的对象所占用的内存空间,避免内存泄漏,确保程序能够高效运行。

1.2 为什么需要垃圾回收?

在C/C++等语言中,开发者需要手动管理内存,这容易导致:

  • 内存泄漏:忘记释放已分配的内存
  • 悬空指针:访问已释放的内存
  • 双重释放:重复释放同一块内存

JVM通过垃圾回收机制自动管理内存,大大降低了内存管理的复杂性。

2. 垃圾回收的基本概念

2.1 对象存活判定

2.1.1 引用计数法
  • 原理:为每个对象维护一个引用计数器
  • 优点:实现简单,判断效率高
  • 缺点:无法解决循环引用问题
// 循环引用示例classA{Bb;}classB{Aa;}
2.1.2 可达性分析算法
  • 原理:从GC Roots出发,通过引用链遍历所有可达对象
  • GC Roots包括
    • 虚拟机栈中引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中JNI引用的对象

2.2 对象的引用类型

引用类型回收条件使用场景
强引用永远不会被回收普通对象引用
软引用内存不足时回收缓存
弱引用下次GC时回收缓存、监听器
虚引用随时可能被回收跟踪对象回收事件

2.3 对象的生命周期

创建 → 可用 → 不可见 → 不可达 → 回收
  1. 创建阶段:对象被创建并初始化
  2. 可用阶段:对象被引用,可以被程序访问
  3. 不可见阶段:对象的引用超出作用域
  4. 不可达阶段:对象不再被任何GC Root引用
  5. 回收阶段:垃圾回收器回收对象占用的内存

3. 垃圾回收算法

3.1 标记-清除算法(Mark-Sweep)

3.1.1 算法原理
  1. 标记阶段:遍历所有对象,标记存活的对象
  2. 清除阶段:遍历所有对象,回收未标记的对象
3.1.2 优缺点
  • 优点:实现简单
  • 缺点
    • 效率问题:标记和清除效率都不高
    • 空间问题:产生大量内存碎片

3.2 复制算法(Copying)

3.2.1 算法原理
  1. 将内存分为两块,每次只使用其中一块
  2. GC时将存活对象复制到另一块内存
  3. 清除当前使用的内存块
3.2.2 优缺点
  • 优点
    • 没有内存碎片
    • 分配内存时只需移动堆顶指针,效率高
  • 缺点:内存利用率只有50%

3.3 标记-整理算法(Mark-Compact)

3.3.1 算法原理
  1. 标记阶段:标记所有存活的对象
  2. 整理阶段:将存活对象向内存一端移动,然后清理边界外的内存
3.3.2 优缺点
  • 优点:没有内存碎片,内存利用率高
  • 缺点:移动对象需要更新引用,效率较低

3.4 分代收集算法(Generational Collection)

3.4.1 堆内存分代

JVM将堆内存分为三个区域:

  • 新生代(Young Generation):新创建的对象存放在这里
    • Eden区:对象首先在这里创建
    • Survivor区:存放经过GC后存活的对象
      • From Space
      • To Space
  • 老年代(Old Generation):经过多次GC后仍然存活的对象
  • 永久代(Permanent Generation):存放类信息、常量、静态变量(JDK 8后被元空间取代)
3.4.2 各代GC策略
  • 新生代:使用复制算法
    • 当Eden区满时,触发Minor GC
    • 存活对象被复制到Survivor区
    • 对象每经过一次GC,年龄加1
    • 年龄达到阈值(默认15)后,晋升到老年代
  • 老年代:使用标记-清除或标记-整理算法
    • 当老年代空间不足时,触发Major GC
    • Major GC通常伴随Minor GC(Full GC)

4. 垃圾收集器

4.1 Serial收集器

4.1.1 特点
  • 单线程收集
  • 收集时暂停所有用户线程(Stop The World)
  • 简单高效,适合客户端模式
4.1.2 工作流程
用户线程 → GC(暂停用户线程) → 用户线程 → GC(暂停用户线程)...

4.2 ParNew收集器

4.2.1 特点
  • Serial收集器的多线程版本
  • 多个线程并行进行垃圾回收
  • 仍然需要暂停用户线程
4.2.2 工作流程
用户线程 → GC(暂停用户线程,多线程并行回收) → 用户线程...

4.3 Parallel Scavenge收集器

4.3.1 特点
  • 关注吞吐量(用户代码运行时间 / 总时间)
  • 自适应调节策略
  • 适合后台任务
4.3.2 关键参数
  • -XX:MaxGCPauseMillis:最大GC停顿时间
  • -XX:GCTimeRatio:GC时间占比
  • -XX:+UseAdaptiveSizePolicy:自适应调节策略

4.4 Serial Old收集器

4.4.1 特点
  • Serial收集器的老年代版本
  • 单线程,使用标记-整理算法

4.5 Parallel Old收集器

4.5.1 特点
  • Parallel Scavenge的老年代版本
  • 多线程,使用标记-整理算法

4.6 CMS收集器(Concurrent Mark Sweep)

4.6.1 特点
  • 以最短停顿时间为目标
  • 并发收集,低停顿
  • 使用标记-清除算法
4.6.2 工作流程
初始标记(STW) → 并发标记 → 重新标记(STW) → 并发清除
4.6.3 优缺点
  • 优点:并发收集,低停顿
  • 缺点
    • 对CPU资源敏感
    • 无法处理浮动垃圾
    • 产生内存碎片

4.7 G1收集器(Garbage-First)

4.7.1 特点
  • 面向服务端应用
  • 将堆划分为多个Region
  • 可预测的停顿时间
  • 整体使用标记-整理算法,局部使用复制算法
4.7.2 工作流程
初始标记 → 并发标记 → 最终标记 → 筛选回收
4.7.3 关键参数
  • -XX:+UseG1GC:启用G1收集器
  • -XX:MaxGCPauseMillis:目标停顿时间
  • -XX:G1HeapRegionSize:Region大小

4.8 ZGC收集器

4.8.1 特点
  • 超低延迟(停顿时间不超过10ms)
  • 支持TB级堆内存
  • 使用着色指针和读屏障技术
4.8.2 工作流程
初始标记 → 并发标记 → 并发预备重分配 → 并发重分配 → 并发重映射

5. 垃圾回收调优

5.1 关键参数

5.1.1 内存相关
  • -Xms:初始堆大小
  • -Xmx:最大堆大小
  • -Xmn:新生代大小
  • -XX:SurvivorRatio:Eden与Survivor区比例
  • -XX:NewRatio:老年代与新生代比例
5.1.2 收集器相关
  • -XX:+UseSerialGC:使用Serial收集器
  • -XX:+UseParNewGC:使用ParNew收集器
  • -XX:+UseParallelGC:使用Parallel Scavenge收集器
  • -XX:+UseConcMarkSweepGC:使用CMS收集器
  • -XX:+UseG1GC:使用G1收集器
  • -XX:+UseZGC:使用ZGC收集器
5.1.3 GC日志
  • -verbose:gc:输出GC日志
  • -XX:+PrintGCDetails:打印GC详细信息
  • -XX:+PrintGCDateStamps:打印GC时间戳
  • -Xloggc:gc.log:GC日志输出文件

5.2 调优原则

5.2.1 选择合适的收集器
  • 客户端应用:Serial收集器
  • 服务端应用:G1收集器
  • 对延迟敏感:ZGC收集器
5.2.2 合理设置堆大小
  • 新生代大小:堆的1/3到1/2
  • 老年代大小:堆的1/2到2/3
  • 避免频繁Full GC
5.2.3 监控GC活动
  • 使用JVisualVM、JConsole等工具
  • 分析GC日志
  • 监控堆内存使用情况

5.3 常见问题

5.3.1 频繁Full GC

可能原因

  • 老年代空间不足
  • 大对象直接进入老年代
  • 内存泄漏

解决方案

  • 增加老年代空间
  • 优化代码,减少大对象创建
  • 检查内存泄漏
5.3.2 GC停顿时间过长

可能原因

  • 堆内存过大
  • 对象图结构复杂
  • 收集器选择不当

解决方案

  • 选择合适的收集器(如G1、ZGC)
  • 调整GC停顿时间目标
  • 优化对象结构

6. 总结

6.1 垃圾回收机制的重要性

  • 自动管理内存,降低开发复杂度
  • 避免内存泄漏和悬空指针
  • 提高程序稳定性和可靠性

6.2 选择垃圾收集器的考虑因素

  • 延迟要求:低延迟选G1或ZGC
  • 吞吐量要求:高吞吐量选Parallel
  • 内存大小:大内存选G1或ZGC
  • 应用类型:客户端选Serial,服务端选G1

6.3 未来发展趋势

  • 更智能的垃圾回收算法
  • 更低的停顿时间
  • 更好的内存利用效率
  • 与容器化技术的深度集成
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 3:46:44

队列原理与实战:从循环队列、阻塞队列到消息队列全梳理

1. 核心能力速览这次我们不聊具体某个开源库,而是把“队列”这个被高频使用的数据结构,从线程池、消息中间件、日志系统到业务削峰,完整梳理一遍。很多读者写业务代码时能熟练使用队列,但一旦遇到“如何选型”“如何避免重复消费”…

作者头像 李华
网站建设 2026/9/7 3:46:02

DecryptAds:用区块链与隐私计算重构广告技术信任体系

Ad Tech 行业乱了太久,DecryptAds 想从根上解决问题广告技术行业(Ad Tech)在过去十几年里发展得异常迅猛,但与此同时,它也背上了不少历史包袱。投放链路长、中间环节多、数据不透明、广告欺诈频发,再加上用…

作者头像 李华
网站建设 2026/9/7 3:43:30

Agent Skills开发实战:大模型应用中的技能封装与工具调用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 3:43:15

微机原理与接口技术核心总结:从8086寻址到中断与接口芯片

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华