核心是盯住回收前后各区域占用量趋势,尤其拆分年轻代和老年代:老年代“回收后占用”持续上升、释放量递减是内存泄漏直接信号;minor gc频率飙升、对象直奔老年代、ygct/ygc激增提示泄漏前兆;metaspace稳定不等于无泄漏,需查classloader实例增长。

分析GC日志中的内存变化,核心是盯住“回收前后各区域的占用量趋势”,尤其要拆开看年轻代和老年代,不能只看总堆(Heap)数字——它容易被新生代波动掩盖真实问题。
重点看老年代回收后占用是否持续上升
内存泄漏最直接的信号不是某次GC失败,而是老年代在多次Full GC后,“回收后占用”逐次升高、释放量越来越小。例如:
- JDK 8 日志中找 [PSOldGen: 6800K->7150K(8192K)] 这类字段,连续几次出现 6800K → 7150K → 7620K,而总大小(括号内)始终不变,说明每次只挤出几十KB,几乎不释放;
- JDK 11+ 统一日志中搜索 Old 或 tenured 标签段,关注 “used after GC” 数值是否阶梯式上涨;
- 若老年代使用率长期 >90%,且每次Full GC后回落幅度小于5%,基本可判定存在对象滞留。
拆解年轻代行为,识别泄漏前兆
Minor GC频率飙升常是泄漏的早期反射,背后往往是大量本该短命的对象因强引用滞留,快速晋升到老年代:
- 观察Eden区是否几秒内就填满,Minor GC间隔从分钟级缩至数秒;
- 开启 -XX:+PrintTenuringDistribution 后,若日志反复显示 age 1: xxx bytes 占比超80%,说明对象几乎不经历Survivor就直奔老年代;
- 配合 jstat -gc
查看:YGCT(Young GC总耗时)和YGC次数在10分钟内激增3倍以上,而OGCMN/OGCMX(老年代容量)未变,大概率有对象在“抢道晋升”。
对比GC前后各区域数值,确认回收有效性
每条GC日志都包含回收前后的内存快照,需横向比对关键字段:
- 年轻代:如 [PSYoungGen: 102400K->16384K(122880K)],看“->”左侧(回收前)是否频繁接近右括号值(最大容量),右侧(回收后)是否明显偏高(比如长期卡在80%以上),反映Survivor区无效对象堆积;
- 整个堆:如 102400K->32768K(349568K),若“->”右侧数值随时间推移整体抬升,尤其伴随Full GC增多,说明内存净增长;
- 触发原因:频繁出现 (Allocation Failure) 触发Full GC,但老年代回收后仍逼近上限,说明新对象持续晋升且无法清理。
别被Metaspace稳定迷惑
看到日志里 Metaspace: XXXK->XXXK 纹丝不动,不代表没有类加载泄漏:
- Metaspace本身不泄漏,但自定义类加载器未释放时,其加载的类实例(如 com.example.ServiceImpl)占的是堆内存,不是Metaspace;
- 此时应检查 ClassLoader实例数是否持续增长,可通过堆转储(heap dump)或JMX查看;
- 若Full GC频繁但Metaspace无变化,反而要优先排查业务代码中是否用了 new URLClassLoader 且未 close。










