java 虚拟机(JVM)的垃圾回收(GC)机制是自动管理内存的关键功能之一,旨在回收不再使用的对象所占用的堆内存。
1.为什么需要 GC
- 内存管理复杂性:手动管理内存(如 C/C++)容易导致内存泄漏和野指针。
- 自动化内存管理:GC 负责自动释放无用对象的内存,减少开发者负担。
- 提高程序可靠性:避免程序因内存分配问题崩溃。
2.JVM 中的内存分代
JVM 将堆内存分为多个区域,以便更高效地进行垃圾回收。
-
新生代(Young Generation)
(1)包含三个区域:Eden 区和两个 Survivor 区(S0, S1)。
(2)对象在此被创建,绝大多数对象很快就会被回收。
(3)GC 类型:Minor GC
1)回收新生代中存活时间短的对象。
2)如果对象存活下来,会被移至 Survivor 区。 -
老年代(Old Generation)
(1)存储生命周期较长的对象(如长时间存活的缓存数据)。
(2)GC 类型:Major GC / Full GC
1)回收老年代中长期存活的对象。
2)Full GC 会同时检查和回收整个堆内存(新生代 + 老年代)。 -
方法区(非堆内存的一部分)
(1)存储类元数据、静态变量、运行时常量池。
(2)垃圾回收的频率较低,但需要回收废弃的类和方法。
3.GC 判定标准(触发时机)
- 可达性分析算法(Reachability Analysis)
(1) 以一组称为 GC Roots 的对象作为起点,向下搜索可达的对象。
(2) 如果一个对象无法通过 GC Roots 被引用到,则被判定为垃圾。
(3) GC Roots 包括:
1)方法栈中的局部变量。
2)活跃线程的静态变量。
3)JNI 引用的对象。 - 引用类型
(1)强引用(Strong Reference):不会被 GC 回收,除非引用被置为 null。
(2)软引用(Soft Reference):内存不足时会回收。
(3)弱引用(Weak Reference):下次 GC 一定会回收。
(4)虚引用(Phantom Reference):仅用于监控对象是否被回收。
4.GC 执行过程
- Minor GC(新生代 GC)
(1)回收新生代的无用对象。
(2)存活对象被移动到 Survivor 区或老年代。
(3)通常速度快,频率高。 - ) Major GC / Full GC(老年代 GC)
(1)回收老年代和新生代中的无用对象。
(2)耗时较长,且会暂停应用线程(Stop-The-World)。
(3)应尽量减少 Full GC 的触发。
5.常见的 GC 垃圾回收器
JVM 提供了多种垃圾回收器,适用于不同的场景:
- Serial GC
单线程垃圾回收,适用于单核 CPU 和小型应用。 - Parallel GC
多线程垃圾回收,适合高吞吐量场景。 - CMS GC(Concurrent Mark-Sweep)
并发标记和清理,减少停顿时间,适合低延迟场景。 - G1 GC(Garbage First)
将堆划分为多个区域,根据回收收益优先回收。
减少 Full GC 的频率,适合大堆内存和低停顿需求的场景。 - ZGC 和 Shenandoah
低延迟 GC,支持超大堆内存,停顿时间通常在毫秒级。
6.JVM 垃圾回收的优化
- 减少对象的生命周期
(1)避免创建过多短命对象。
(2)使用对象池(如线程池、连接池)。 - 合理配置堆内存
(1)新生代和老年代的比例可通过 JVM 参数调整,例如:
设置 JVM 堆内存的初始大小为 512 MB, JVM 堆内存的最大大小为 1024 MB,新生代与老年代的堆内存比例为 1:2。
-Xms512m -Xmx1024m -XX:NewRatio=2
- 优化垃圾回收器的选择
(1)根据需求选择合适的回收器(如低延迟或高吞吐量)。 - 减少 Full GC
(1)避免大对象频繁分配到老年代。
(2)调整 GC 参数,例如:
G1 GC 将堆划分为多个小区域(Region),动态选择高回收效率的区域进行回收。
-XX:+UseG1GC
7.垃圾回收的监控与调试
使用工具监控垃圾回收的运行状况:
- VisualVM:可视化监控堆内存使用和 GC 活动。
- JConsole:监控 JVM 的运行状态。
- GC 日志:通过 JVM 参数启用,例如:
在控制台打印详细的垃圾回收日志(包含:每次垃圾回收的类型(Minor GC、Major GC 等),回收前后堆内存的使用情况,回收所花费的时间等信息)。将 GC 日志保存到指定文件(gc.log)
-XX:+PrintGCDetails -Xloggc:gc.log