欢迎光临
我们一直在努力

JVM垃圾回收机制详解

JVM垃圾回收机制详解

1. 引言

1.1 什么是垃圾回收机制?

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

1.2 为什么需要垃圾回收?

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

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

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

2. 垃圾回收的基本概念

2.1 对象存活判定

2.1.1 引用计数法
  • 原理:为每个对象维护一个引用计数器
  • 优点:实现简单,判断效率高
  • 缺点:无法解决循环引用问题

// 循环引用示例
class A {
B b;
}
class B {
A a;
}

2.1.2 可达性分析算法
  • 原理:从GC Roots出发,通过引用链遍历所有可达对象
  • GC Roots包括:
    • 虚拟机栈中引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中JNI引用的对象

2.2 对象的引用类型

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

2.3 对象的生命周期

创建 → 可用 → 不可见 → 不可达 → 回收

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

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

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

    3.2 复制算法(Copying)

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

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

    3.3.1 算法原理
  • 标记阶段:标记所有存活的对象
  • 整理阶段:将存活对象向内存一端移动,然后清理边界外的内存
  • 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 未来发展趋势

    • 更智能的垃圾回收算法
    • 更低的停顿时间
    • 更好的内存利用效率
    • 与容器化技术的深度集成
    赞(0)
    未经允许不得转载:171主机测评 » JVM垃圾回收机制详解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址