避免频繁full gc需从对象生命周期、jvm配置和运行时行为三方面主动拦截:阻断大对象直入老年代,收紧对象创建与持有习惯,合理调优jvm参数,并依托日志与工具精准定位问题根源。

避免频繁 Full GC,关键不是“等它来了再救”,而是从对象生命周期、JVM配置和运行时行为三方面主动拦截——不让大对象乱进老年代,不让内存泄漏悄悄堆积,也不让参数配置拖后腿。
阻断大对象直入老年代
大对象(如 >4MB 的数组、超长 JSON 字符串、百万级 List)默认绕过年轻代,直接分配到老年代。一旦老年代空间吃紧或碎片化,立刻触发 Full GC。
- 设置 -XX:PretenureSizeThreshold=4194304(例如 4MB),让只有真正超大的对象才走直入路径;中小规模的大对象仍可走 Eden → Survivor → 老年代路径,便于 Minor GC 提前清理
- 用流式替代全量加载:接口返回不拼完整 JSON 字符串,改用
ObjectMapper.writeValue(OutputStream, obj)直写响应流;导出文件不用list.stream().collect(toList()),改用分批forEach+ flush - 禁用无上限缓存:比如
static Map<string byte></string>缓存文件内容,必须加大小限制 + LRU 清理逻辑,否则就是老年代定时炸弹
收紧对象创建与持有习惯
很多 Full GC 是“小对象攒多了”或“该释放没释放”导致的——它们未必单个很大,但长期滞留老年代,最终挤爆空间。
- 复用高频中间对象:用
ThreadLocal<stringbuilder></stringbuilder>替代每次 new;用对象池管理连接、缓冲区等重量级实例 - 检查静态引用:
static List、static Map、未清理的ThreadLocal变量,都是典型的内存泄漏温床 - 及时关闭资源:数据库连接、文件流、网络 socket,漏关一个就可能拖慢整个老年代回收节奏
调优 JVM 参数组合
参数不是调得越细越好,而是要匹配业务特征——高吞吐选 Parallel,低延迟选 G1 或 ZGC,堆结构要给年轻代留足“缓冲带”。
- 固定堆大小:-Xms4g -Xmx4g,避免动态扩容引发的 Full GC
- 增大年轻代比例:-XX:NewRatio=2(老:新 = 2:1)或直接设 -Xmn1536m,让短期对象尽量在 Minor GC 阶段被清掉
- G1 场景下启用 -XX:+UseG1GC -XX:G1HeapRegionSize=1M,提升大对象(Humongous Object)分配可控性;同时禁用 -XX:+DisableExplicitGC 防止
System.gc()干扰
靠日志和工具定位真问题
凭感觉调参不如看数据说话。Full GC 频繁从来不是单一原因,必须结合 GC 日志、堆快照和业务流量交叉验证。
- 开启详细 GC 日志:-Xlog:gc*,gc+age=trace,safepoint,重点关注 “Promotion Failure”、“Concurrent Mode Failure” 等关键词
- 用 MAT 分析 heap dump,按“Retained Heap”排序,找出实际占用最大的对象及其 GC Root 引用链
- 监控老年代使用率趋势 + Full GC 触发时间点,比对是否与某次上线、某个定时任务、某类批量接口强相关










