写 Java 代码时你可能写过这样的代码while(true){Listbyte[]listnewArrayList();for(inti0;i1000;i){list.add(newbyte[1024*1024]);// 1MB}Thread.sleep(1000);}运行一段时间后程序可能会卡顿甚至OOM。这背后就是垃圾回收GC在起作用。理解 GC能帮你排查线上 OOM 问题优化程序性能减少 GC 停顿合理配置 JVM 参数选型合适的垃圾收集器下面我按「分代模型 → GC 算法 → 垃圾收集器 → GC 日志」的顺序往下聊。1. 为什么需要 GC1.1 内存泄漏与 OOMJava 程序运行时会不断创建对象这些对象存在堆内存中。如果对象不再使用但没有被回收堆内存会逐渐耗尽最终导致OutOfMemoryError。// 内存泄漏示例Listbyte[]listnewArrayList();while(true){list.add(newbyte[1024*1024]);// 不断添加不清理Thread.sleep(1000);}1.2 GC 的作用GCGarbage Collection自动回收不再使用的对象释放内存空间。// GC 自动回收Listbyte[]listnewArrayList();for(inti0;i1000;i){list.add(newbyte[1024*1024]);}// 循环结束后list 超出作用域变成垃圾// GC 会自动回收这些 byte[] 对象2. JVM 分代模型 2.1 堆内存分代JVM 堆内存分为新生代和老年代堆内存Heap │ ├─ 新生代Young Generation │ ├─ Eden 区新对象分配 │ ├─ Survivor 区 S0From │ └─ Survivor 区 S1To │ └─ 老年代Old Generation └─ 长期存活的对象2.2 新生代新生代存放新创建的对象大多数对象生命周期很短。Eden 区新对象分配区Survivor 区S0、S1存放经历一次 Minor GC 后仍然存活的对象// 大多数对象在新生代分配UserusernewUser();// 在 Eden 区2.3 老年代老年代存放长期存活的对象经历多次 Minor GC 后仍然存活的对象大对象直接分配// 大对象直接进入老年代byte[]largenewbyte[10*1024*1024];// 10MB2.4 元空间方法区元空间Java 8存放类信息、常量、静态变量Java 7 及之前永久代PermGen Java 8 及之后元空间Metaspace3. GC 类型与触发条件 3.1 Minor GCYoung GCMinor GC发生在新生代清理 Eden 区和 Survivor 区。触发条件Eden 区空间不足特点频率高停顿时间短采用复制算法3.2 Major GC / Full GCMajor GC发生在老年代清理老年代。Full GC清理整个堆新生代 老年代。触发条件老年代空间不足调用System.gc()元空间不足Minor GC 后晋升对象大小 Survivor 区特点频率低停顿时间长影响系统性能3.3 GC 日志示例[GC (Allocation Failure) [PSYoungGen: 512K-64K(1536K)] 1024K-512K(2048K), 0.0012345 secs]4. GC 算法 4.1 标记-清除算法Mark-Sweep步骤标记所有需要回收的对象清除标记的对象缺点产生内存碎片效率不高标记-清除 标记前□ ■ □ ■ ■ □ 标记后□ ■ ■ ■ ■ □ ■ 是垃圾 清除后□ □ □ □ □ 碎片4.2 复制算法Copying将内存分为两块每次只使用一块回收时将存活对象复制到另一块。优点无碎片缺点内存利用率低复制算法 ┌─────────┬─────────┐ │ 使用中 │ 空闲 │ └─────────┴─────────┘ 回收时把存活对象复制到空闲区4.3 标记-整理算法Mark-Compact步骤标记需要回收的对象整理存活对象向一端移动清除边界外的对象优点无碎片缺点效率低标记-整理 标记前□ ■ □ ■ ■ □ 整理后□ □ □ □ □ ■ 存活对象移动到一端 清除后□ □ □ □ □5. 垃圾收集器 5.1 收集器分类收集器作用区域算法特点Serial新生代复制单线程停顿长ParNew新生代复制多线程版本Parallel Scavenge新生代复制吞吐量优先Serial Old老年代标记-整理单线程Parallel Old老年代标记-整理多线程CMS老年代标记-清除并发收集低停顿G1全堆标记-整理分区域可预测停顿ZGC全堆标记-整理并发极低停顿5.2 Serial 收集器最古老的收集器单线程执行。# 启用 Serial 收集器-XX:UseSerialGC特点单线程简单高效停顿时间长适用场景客户端模式、内存较小的应用5.3 Parallel 收集器吞吐量优先的收集器多线程并行收集。# 启用 Parallel 收集器-XX:UseParallelGC# 设置线程数-XX:ParallelGCThreads4特点多线程吞吐量高适合后台应用适用场景批处理、科学计算5.4 CMS 收集器并发收集器低停顿。# 启用 CMS 收集器-XX:UseConcMarkSweepGC工作阶段初始标记STW标记 GC Roots并发标记并发追踪存活对象重新标记STW修正标记并发清除清除垃圾特点并发收集停顿短产生内存碎片适用场景互联网应用5.5 G1 收集器面向服务端的收集器可预测停顿。# 启用 G1 收集器-XX:UseG1GC# 设置停顿目标-XX:MaxGCPauseMillis200特点分区域Region可预测停顿整理碎片并发收集适用场景大内存应用5.6 ZGC 收集器超低停顿的收集器。# 启用 ZGC-XX:UseZGC特点并发收集停顿 10ms支持 TB 级内存适用场景大内存、低停顿应用5.7 收集器组合# 组合 1Serial Serial Old-XX:UseSerialGC# 组合 2ParNew CMS-XX:UseParNewGC-XX:UseConcMarkSweepGC# 组合 3Parallel Parallel Old-XX:UseParallelGC# 组合 4G1独立使用-XX:UseG1GC6. GC 日志解读 6.1 开启 GC 日志# 打印 GC 日志-XX:PrintGCDetails-XX:PrintGCDateStamps-Xloggc:gc.log# 示例java-XX:PrintGCDetails-Xloggc:gc.logMyApp6.2 Minor GC 日志[GC (Allocation Failure) [PSYoungGen: 512K-64K(1536K)] // 新生代回收前-回收后(总大小) 1024K-512K(2048K), // 堆回收前-回收后(总大小) 0.0012345 secs] // 停顿时间6.3 Full GC 日志[Full GC (Allocation Failure) [PSYoungGen: 512K-0K(1536K)] [ParOldGen: 1024K-1023K(2048K)] 1536K-1023K(3584K), 0.0123456 secs]6.4 CMS 日志[GC [1 CMS-initial-mark: 1024K(2048K)] 1024K(2048K), 0.0012345 secs] [GC [CMS-concurrent-mark-start] [GC [CMS-concurrent-mark: 0.0123456/0.0123456 secs] [GC [CMS-concurrent-sweep-start] [GC [CMS-concurrent-sweep: 0.0234567/0.0234567 secs] [GC [CMS-concurrent-reset-start] [GC [CMS-concurrent-reset: 0.0001234/0.0001234 secs]6.5 G1 日志[GC pause (G1 Evacuation Pause) young 512M-128M(2048M), 0.0123456 secs] [GC concurrent-root-region-scan-start] [GC concurrent-root-region-scan-end, 0.0012345 secs] [GC concurrent-mark-start] [GC concurrent-mark-end, 0.0234567 secs]7. GC 调优实战 7.1 常见 GC 问题问题现象原因Minor GC 频繁频繁 minor gc对象分配太快Full GC 频繁频繁 full gc内存不足、大对象GC 停顿长页面卡顿堆太大、GC 选型不当OOM程序崩溃内存泄漏、堆太小7.2 调优思路# 1. 调整堆大小-Xms2g-Xmx2g# 2. 调整新生代比例-Xmn512m# 新生代 512MB-XX:NewRatio2# 新生代:老年代 1:2# 3. 调整 Survivor 比例-XX:SurvivorRatio8# Eden:Survivor 8:1# 4. 选择收集器-XX:UseG1GC-XX:MaxGCPauseMillis2007.3 监控工具# jstat查看 GC 统计jstat-gcutilpid1000# jmap查看堆内存jmap-heappid# jcmd综合诊断jcmdpidGC.heap_info小结GC自动回收不再使用的对象释放内存分代模型新生代Eden Survivor、老年代、元空间GC 算法标记-清除、复制、标记-整理垃圾收集器Serial、Parallel、CMS、G1、ZGCCMS并发收集低停顿适合互联网应用G1分区域收集可预测停顿适合大内存应用GC 日志可以反映 GC 频率、停顿时间、内存使用情况下一篇024预告JVM 参数入门堆、栈、元空间与典型模板——常用 JVM 参数、GC 调优参数、性能优化模板。