垃圾收集的基本概念
垃圾收集(Garbage Collection, GC) 是 JVM 自动管理内存的机制,负责回收不再使用的对象所占用的内存。
关键概念
-
垃圾对象:不再被任何引用指向的对象
-
GC Roots:始终可达的对象,作为引用链的起点
-
Stop-the-World:GC 执行时暂停所有应用线程
内存分代模型
JVM 将堆内存分为三个主要区域:
1. 年轻代 (Young Generation)
-
Eden区:新创建的对象首先分配在这里
-
Survivor区 (S0, S1):经历 Minor GC 后存活的对象在这里交换
2. 老年代 (Old Generation)
-
长期存活的对象最终会晋升到这里
-
需要 Major GC/Full GC 来清理
3. 元空间 (Metaspace) - JDK 8+
-
存储类元数据、方法信息等
-
替代了之前的永久代 (PermGen)
垃圾收集算法
1. 标记-清除 (Mark-Sweep)
// 概念性代码 public class MarkSweep {public void garbageCollect() {// 阶段1: 标记 - 找出所有存活对象 markFromRoots();// 阶段2: 清除 - 回收未标记对象的内存 sweep();}private void markFromRoots() {// 从GC Roots开始遍历所有可达对象并标记 }private void sweep() {// 遍历堆内存,回收未标记对象 } }
2. 复制算法 (Copying)
-
将内存分为两块,只使用其中一块。GC 时将存活对象复制到另一块,然后清空当前块。
-
用于年轻代的 Survivor 区。
3. 标记-整理 (Mark-Compact)
-
先标记所有存活对象。然后将存活对象向一端移动,清理边界以外的内存。
-
用于老年代。
主要的垃圾收集器
1. Serial GC
-
单线程收集器。适合客户端应用、小内存场景
-XX:+UseSerialGC
2. Parallel GC (吞吐量优先)
-
多线程并行收集。JDK 8 默认收集器
-XX:+UseParallelGC
3. CMS (Concurrent Mark Sweep)
-
尽量减少停顿时间。与应用程序并发执行
-XX:+UseConcMarkSweepGC
4. G1 (Garbage First) - JDK 9+ 默认
// G1 的堆内存布局 public class G1Layout {// 将堆划分为多个等大小的Region (1MB-32MB)// 不再固定分代大小,动态分配// 优先回收垃圾最多的Region (Garbage First) }
5. ZGC (低延迟)
-
目标:停顿时间不超过10ms。支持TB级别堆内存
-XX:+UseZGC
6. Shenandoah
-
类似ZGC的低延迟收集器。与应用程序并发执行
-XX:+UseShenandoahGC
GC 触发时机
1. Minor GC
-
当Eden区满时触发。只收集年轻代。频率高,停顿时间短
2. Major GC / Full GC
-
当老年代满时触发。收集整个堆(年轻代 + 老年代)。频率低,停顿时间长
对象生命周期
public class ObjectLifecycle {public void demonstrate() {// 1. 对象在Eden区创建Object obj1 = new Object();// 2. 经历Minor GC后,如果存活则进入Survivor区// 每经历一次Minor GC,年龄+1// 3. 当年龄达到阈值(默认15),晋升到老年代for (int i = 0; i < 15; i++) {System.gc(); // 模拟多次GC }// 4. 最终在Full GC时被回收(如果没有引用)obj1 = null; // 使对象成为垃圾System.gc(); // 建议JVM执行GC } }
常见的 GC 调优参数
# 设置堆大小 -Xms512m -Xmx2g# 设置年轻代大小 -XX:NewRatio=2 # 老年代:年轻代 = 2:1 -XX:NewSize=256m -XX:MaxNewSize=512m# 设置Survivor区比例 -XX:SurvivorRatio=8 # Eden:Survivor = 8:1:1# 选择GC算法 -XX:+UseG1GC -XX:+UseParallelGC -XX:+UseZGC# GC日志配置 -Xlog:gc*:file=gc.log:time,uptime,level,tags -XX:+PrintGCDetails -XX:+PrintGCDateStamps
最佳实践
-
及时释放引用:对象不再使用时设为null
-
避免大对象:大对象直接进入老年代
-
合理使用集合:及时清理不再需要的集合数据
-
使用局部变量:方法结束后自动释放
-
谨慎使用静态集合:容易导致内存泄漏
监控工具
-
jstat:监控GC统计信息
-
jmap:堆内存分析
-
VisualVM:图形化监控
-
GC日志:分析GC行为和性能