深入理解 Java 虚拟机之垃圾收集

垃圾收集(Garbage Collection,GC)是 Java 虚拟机(JVM)内存管理的核心机制,主要针对 Java 堆方法区 进行回收。线程私有的程序计数器、虚拟机栈和本地方法栈随线程生命周期结束而自然消失,无需 GC。本文将详细探讨 GC 的判定依据、算法、收集器及内存分配策略。


对象是否需要回收

1. 引用计数算法

  • 原理:对象维护一个引用计数器,被引用时加 1,引用失效时减 1,计数为 0 时可回收。
  • 缺陷:无法解决循环引用问题,例如两个对象互相引用,计数永不为 0。
  • 结论:JVM 不采用此算法。

2. 可达性分析算法

  • 原理 :从 GC Roots 开始搜索,可达对象存活,不可达对象死亡。
  • GC Roots
    • 虚拟机栈中的引用对象。
    • 本地方法栈中的引用对象。
    • 方法区中的静态属性和常量引用对象。
  • 应用:JVM 主流采用此算法。

3. 引用类型

Java 提供四种引用类型,影响对象回收:

  • 强引用new 创建的对象,不会回收。
  • 软引用SoftReference,内存不足时回收。
  • 弱引用WeakReference,下次 GC 时回收,常用于缓存(如 WeakHashMap)。
  • 虚引用PhantomReference,仅用于接收回收通知。

4. 方法区回收

  • 目标:废弃常量和无用类。
  • 类卸载条件
    1. 所有实例已回收。
    2. 类加载器已回收。
    3. Class 对象未被引用。
  • 控制 :通过 -Xnoclassgc 参数决定是否卸载。

5. finalize() 方法

  • 作用:对象回收前可执行的自救方法。
  • 问题:运行代价高、不确定性大,建议避免使用。

垃圾收集算法

1. 性能指标

  • 停顿时间:GC 导致的程序暂停时长。
  • 吞吐量:用户代码运行时间占比。

2. 标记-清除(Mark-Sweep)

  • 过程:标记需回收对象,然后清除。
  • 缺点:效率低,产生内存碎片。

3. 标记-整理(Mark-Compact)

  • 过程:标记后将存活对象移至一端,清理边界外内存。
  • 优点:解决碎片问题。
  • 缺点:整理开销大。

4. 标记-复制(Copying)

  • 过程:内存分两块,存活对象复制到空闲块,清理已用块。
  • 优点:无碎片。
  • 缺点:内存利用率低(改进为 Eden + 2 Survivor)。

5. 分代收集

  • 原理:根据对象生命周期分代,年轻代用复制算法,老年代用标记-清除或标记-整理。
  • 堆结构
    • 新生代:Eden + 2 Survivor,默认比例 8:1:1。
    • 老年代:存放长生命周期对象。
    • 永久代:JDK 8 前的方法区实现,现为元空间。

垃圾收集器

HotSpot 提供多种垃圾收集器,各有适用场景:

1. 串行收集器(Serial)

  • 特点:单线程,Stop-The-World,适用于 Client 模式。
  • 组合
    • Serial :年轻代,复制算法,-XX:+UseSerialGC
    • Serial Old:老年代,标记-整理。

2. 并行收集器

  • 目标:高吞吐量,Server 模式默认。
  • 组合
    • Parallel Scavenge :年轻代,复制算法,-XX:+UseParallelGC
    • Parallel Old :老年代,标记-整理,-XX:+UseParallelOldGC
  • 参数
    • -XX:MaxGCPauseMillis:控制停顿时间。
    • -XX:GCTimeRatio:设置吞吐量。
    • -XX:+UseAdaptiveSizePolicy:自适应调整。

3. 并发标记清除(CMS)

  • 目标:最短停顿时间。
  • 组合-XX:+UseConcMarkSweepGC,ParNew(年轻代)+ CMS(老年代)+ Serial Old(备用)。
  • 步骤
    1. 初始标记(停顿)。
    2. 并发标记。
    3. 重新标记(停顿)。
    4. 并发清除。
  • 缺点:内存碎片、浮动垃圾,需预留空间。

4. G1 收集器

  • 特点 :兼顾吞吐量和停顿时间,JDK 9+ 默认,-XX:+UseG1GC
  • 分区:堆划分为多个 Region,动态分配角色。
  • 步骤
    1. 初始标记。
    2. 并发标记。
    3. 最终标记。
    4. 筛选回收(优先高价值 Region)。
  • 优点:无碎片,可预测停顿。

收集器对比

收集器 类型 算法 目标 场景
Serial 串行 年轻代 复制 低停顿 单核 Client 模式
Serial Old 串行 老年代 标记-整理 低停顿 CMS 备用
ParNew 并行 年轻代 复制 低停顿 与 CMS 配合
Parallel Scavenge 并行 年轻代 复制 高吞吐量 后台运算
Parallel Old 并行 老年代 标记-整理 高吞吐量 后台运算
CMS 并发 老年代 标记-清除 低停顿 Web 服务端
G1 并发 全部 标记-整理+复制 低停顿+吞吐量 服务端应用

内存分配与回收策略

1. Minor GC

  • 触发:Eden 区满。
  • 过程 :存活对象复制到 Survivor,年龄达阈值(默认 15,-XX:MaxTenuringThreshold)晋升老年代。

2. Full GC

  • 触发条件
    1. System.gc()(建议性,可禁用)。
    2. 老年代空间不足。
    3. 方法区(元空间)不足。
    4. Minor GC 晋升平均大小超老年代剩余空间。
    5. 对象大于 Survivor 和老年代可用空间。

3. 分配策略

  • Eden 优先:新对象分配在 Eden。
  • 大对象直入老年代-XX:PretenureSizeThreshold
  • 长期存活晋升:年龄超阈值。
  • 动态年龄判定:Survivor 半满时提前晋升。
  • 空间担保:老年代为 Minor GC 提供担保。

结语

JVM 的垃圾收集机制通过可达性分析、分代收集和多样化的收集器,高效管理内存。串行适合小型应用,并行追求吞吐量,CMS 和 G1 优化停顿时间。理解 GC 原理和策略,有助于调优程序性能,避免内存溢出等问题。

相关推荐
m0_587383003 分钟前
智慧场馆解决方案实战指南:从系统架构到落地部署全解析
java·spring boot·架构·系统架构
我不是疯子是傻子4 分钟前
Qt 程序打包部署全攻略:从 windeployqt 到 Inno Setup 的静默安装与版本迭代实战
开发语言·qt
ZC跨境爬虫8 分钟前
LeetCode 13. 罗马数字转整数(多解法详解 + Java Python 实现)
java·python·leetcode
省长13 分钟前
Sa-Token v1.46.0 发布 🚀,来看看有没有令你心动的功能!
java·后端·开源
LayZhangStrive37 分钟前
融360 一面
java·面试·后端开发
城管不管1 小时前
重生——第十次面试之开源中国一面挂
java·linux·开发语言·算法·面试·职场和发展·开源
Java小白笔记1 小时前
Windows系统免软件命令激活
java·网络·人工智能·windows·ai·ai编程
爱敲键盘的猴子1 小时前
深入理解 Java HashMap
java·hashmap
霸道流氓气质1 小时前
Java开发者AI编程常用MCP、Skills、Rules全景汇总
java·开发语言·ai编程
天涯醉青楼1 小时前
Spring Boot 接口返回 Bean 时字段名“变形”的深度剖析与解决方案
java