异常本身不触发Full GC,但高频创建携带长message和深栈跟踪的异常实例会引发Full GC;需通过GC日志、jmap直方图与MAT分析定位异常对象堆积及持有链。
这个问题本质是混淆了异常机制与内存行为——在循环体内声明(而非抛出)受检异常,本身不会触发 full gc;但若在循环中高频创建并抛出携带超长描述字符串的受检异常对象,就可能成为 full gc 的诱因。关键不在“声明”,而在“构造异常实例 + 填充长栈跟踪 + 持有长 message 字符串”。
下面分三块说清楚怎么排查:
看 GC 日志确认是否真由异常对象驱动
先确保 JVM 启用了详细 GC 日志(Java 8/11 推荐):
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/app/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M
启动后复现问题,重点查:
- Full GC 触发前,老年代占用是否陡升?
- 是否伴随大量
java.lang.Exception、java.io.IOException等子类在堆中堆积? - 日志里有没有
Full GC (Allocation Failure)—— 如果是,说明不是元空间或 System.gc() 导致,而是老年代真被填满了。
用 jmap + MAT 定位异常对象是否霸占老年代
执行:
jmap -histo:live <pid> | head -20</pid>
关注输出前三列:
- #instances:异常类实例数是否异常高(比如几万甚至十万+)
-
bytes:
java.lang.String、java.lang.StackTraceElement[]、异常类自身是否排进 Top 5 -
class name:是否有自定义异常(如
OrderValidationException)或标准异常高频出现
再导出堆快照:
jmap -dump:format=b,file=heap.hprof <pid></pid>
用 Eclipse MAT 打开,做两件事:
- 打开 Histogram → 过滤
java.lang.Exception及其子类 → 看 Retained Heap 是否巨大 - 点击某个异常实例 → 右键 Merge Shortest Paths to GC Roots → 查看谁长期持有着它(常见是静态日志上下文、未清空的线程局部缓存、异步任务队列)
典型线索:
-
message字段是StringBuilder.toString()构造的超长 JSON 或 SQL 片段(>10KB) -
stackTrace数组长度动辄几百层(说明异常被层层包装,如new ServiceException(new DataAccessException(new SQLException(...)))) - 每个异常都持有
ThreadLocal<requestcontext></requestcontext>,而 RequestContext 又引用了整个 HTTP 请求体
检查代码中高频异常构造的模式
这类问题往往藏在这些位置:
- 在 for 循环或 while 循环里,对每条数据做校验,失败就
throw new BizException("校验失败,原始数据:" + JSON.toJSONString(data)) - 使用
String.format拼接超长 message,且 data 是 Map/List 等嵌套结构 - 异常被 catch 后未处理,又原样 re-throw,导致栈跟踪不断叠加
- 日志框架(如 Logback)配置了
%ex{full},每次打印都触发异常对象深度序列化
改法很简单:
-
把「描述性信息」从 message 移到 log 语句里,异常只留简短 code 或关键词:
// ❌ 错误:每次构造都生成 MB 级字符串 throw new ValidationException("用户ID=" + userId + ", 订单列表=" + JSON.toJSONString(orders)); // ✅ 正确:异常轻量,日志补全上下文 log.warn("订单校验失败,userId={}, orders.size={}", userId, orders.size()); throw new ValidationException("VALIDATION_FAILED"); 避免在异常构造时调用
JSON.toJSONString()、toString()等重量级方法对已知高频失败路径(如参数校验),改用返回
Result<t></t>或Optional,完全绕过异常机制
不复杂但容易忽略。










