Java的垃圾回收(GC)机制是JVM自动管理内存的核心,通过回收不再使用的对象释放堆内存,避免内存泄漏。其核心包括垃圾回收器和内存回收策略,以下从原理、算法、回收器类型及优化策略展开说明:
⚙️ 一、垃圾回收基本原理
- 对象存活判定
- 可达性分析算法:从GC Root(如虚拟机栈局部变量、静态属性、常量等)出发,遍历引用链。未被引用的对象标记为“不可达”,即垃圾。
- 引用类型:
- 强引用:普遍存在,只要存在则不会被回收。
- 软引用:内存不足时回收(适用于缓存)。
- 弱引用:下次GC时必回收。
- 虚引用:仅用于回收通知。
- 方法区回收
主要回收废弃常量(如未被引用的字符串)和无用类(需满足:实例全回收、类加载器已回收、Class对象未被引用)。
🔄 二、垃圾回收算法
- 标记-清除(Mark-Sweep)
- 步骤:标记所有可达对象 → 清除未标记对象。
- 缺点:内存碎片化,影响大对象分配效率。
- 复制算法(Copying)
- 步骤:将内存分为两块,存活对象复制到另一块 → 清空原区域。
- 应用:新生代(Eden区 + 两个Survivor区,默认8:1:1),Minor GC后存活对象移至Survivor。
- 缺点:空间利用率仅50%。
- 标记-整理(Mark-Compact)
- 步骤:标记可达对象 → 向内存一端移动 → 清理边界外空间。
- 应用:老年代,避免碎片化。
- 分代收集(Generational Collection)
- 新生代:对象存活率低,采用复制算法(Minor GC)。
- 老年代:对象存活率高,采用标记-清除或标记-整理(Major GC/Full GC)。
🛠️ 三、主流垃圾回收器及工作原理
回收器 | 特点 | 适用场景 |
Serial GC | 单线程执行GC,全程STW(Stop-The-World) | 客户端/小内存应用 |
Parallel GC | 多线程并行回收,提高吞吐量(-XX:+UseParallelGC) | 后台计算型应用(高吞吐需求) |
CMS GC | 并发标记清除,减少STW时间(分4阶段:初始标记→并发标记→重新标记→并发清除) | 低延迟Web服务 |
G1 GC | 分区堆(Region),优先回收垃圾最多区域;可预测停顿模型 | 大内存、高响应应用(JDK9+默认) |
ZGC/Shenandoah | TB级堆内存支持,停顿时间<10ms,全程并发 | 超低延迟场景(如金融交易) |
注:
- STW(Stop-The-World):GC时暂停所有应用线程。
- 并发 vs 并行:并发指GC与应用线程交替执行;并行指多线程同时GC。
📊 四、内存分配与回收策略
- 对象分配原则
- 新生代Eden分配:多数对象在Eden区创建,空间不足时触发Minor GC。
- 大对象直入老年代:避免在Eden区频繁复制(如长数组)。
- 长期存活进老年代:对象年龄计数器达阈值(默认15)后晋升。
- GC触发条件
- Minor GC:Eden区满时触发。
- Full GC:老年代空间不足、方法区不足、或主动调用
System.gc()
。
⚡ 五、优化建议
- 垃圾回收器选择
- 吞吐优先 → Parallel GC;
- 低延迟 → G1/ZGC;
- 小堆 → Serial GC。
- 关键参数调优
- 堆大小:
-Xms
(初始堆)、-Xmx
(最大堆); - 新生代比例:
-XX:NewRatio
(老年代/新生代比例); - Surviv区调整:
-XX:SurvivorRatio
(Eden/Survivor)。
- 监控工具
- JVisualVM/GC日志分析:监控GC频率、停顿时间、内存泄漏(如对象持续进入老年代)。
💎 总结
Java垃圾回收通过分代模型和多样化回收器平衡吞吐、延迟与内存开销。理解不同回收器特性(如G1的分区回收、ZGC的并发压缩)及内存分配策略(如大对象直进老年代),结合监控调优,可显著提升应用性能。实际部署需根据应用场景(如高并发/大数据量)选择合适组合,避免频繁Full GC导致的系统卡顿。