堆内存过小导致minor gc频繁和full gc增多,需从年轻代分配(-xmn设为堆1/3~1/2)、survivor区调优(-xx:survivorratio=4、-xx:maxtenuringthreshold=6)、堆大小固定(-xms=-xmx)及大对象处理(-xx:pretenuresizethreshold)三方面协同优化。

堆内存设太小,对象很快填满空间,Minor GC 就会高频触发,老年代也容易被提前塞满,进而引发 Full GC。这不是加点参数就能糊弄过去的,得从分配比例、代际结构和实际负载三方面一起调。
先看年轻代是不是真不够用
年轻代太小是高频 Minor GC 最常见的根因。观察 jstat -gc <pid> 1s</pid> 输出:
- 如果每分钟 Minor GC 超过 5~10 次,基本可以判定 Eden 区扛不住了
- 同时注意
YGC(次数)和YGCT(耗时)的比值,单次耗时短但次数爆炸,就是空间瓶颈
建议把-Xmn显式设为堆总大小的 1/3~1/2。比如-Xmx4g,就配-Xmn1.5g或-Xmn2g,别依赖默认值。
再调 Survivor 区防“误晋升”
默认 SurvivorRatio=8(Eden:S0:S1 = 8:1:1),Survivor 总共只占年轻代 1/5,短期对象一多就溢出,直接进老年代。
- 改成
-XX:SurvivorRatio=4(Eden:S0:S1 = 4:1:1),Survivor 总容量翻倍 - 配合
-XX:MaxTenuringThreshold=6,避免对象熬过几次 GC 就被“赶”去老年代
调完后用jstat看 S0/S1 使用率是否更平稳,晋升量(OU增长速率)有没有明显下降。
堆本身也不能太吝啬
-
-Xms和-Xmx必须设成一样,否则堆扩容过程会触发 Full GC - 如果业务稳定后老年代使用率长期高于 70%,说明堆整体偏小或对象生命周期偏长,该加堆了
- 大对象(比如 >3MB 的 byte[])会直入老年代,可加
-XX:PretenureSizeThreshold=2M让它走明路,别偷偷挤占老年代空间
顺手堵几个常见漏洞
- 加
-XX:+DisableExplicitGC,屏蔽代码里残留的System.gc() - 加
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m,防止类加载多导致 Metaspace 溢出触发 Full GC - 日志必须开:
-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps,没日志等于闭眼开车
不复杂但容易忽略











