最快方式是查看GC日志中是否反复出现“Full GC (System)”标记,若存在则说明由显式System.gc()调用触发,而非内存不足或泄漏;可配合-XX:+DisableExplicitGC拦截并用Arthas stack命令精确定位调用源。

直接看 GC 日志里有没有 Full GC (System) 这类标记,是判断是否被 System.gc() 触发的最快方式。只要日志中反复出现这个字样,基本就能锁定问题源头是显式 GC 调用,而不是内存不足或泄漏。
从 GC 日志快速确认 System.gc() 调用
启用标准 GC 日志后(如 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/gc.log),打开日志文件搜索关键词:
-
Full GC (System)—— 最明确的证据,说明 JVM 确实响应了System.gc() -
Full GC (Metadata GC Threshold)或Allocation Failure—— 这些属于正常触发机制,与显式调用无关 - 如果同一时间段内
FGC次数激增,但老年代使用率(O列)几乎不变,也高度提示是System.gc()驱动,而非空间压力
用 -XX:+DisableExplicitGC 拦截调用
这个 JVM 参数的作用不是“忽略”调用,而是让 JVM 直接跳过执行——System.gc() 方法调用后不触发任何 GC 动作,返回即结束。它不会报错,也不会抛异常,对业务代码完全透明。
- 添加方式:在启动参数中加入
-XX:+DisableExplicitGC,建议配合-XX:+PrintGCDetails一起使用,便于验证是否生效 - 生效验证:加参数重启后,再观察 GC 日志——若之前频繁出现的
Full GC (System)消失,且FGC计数趋于平稳,说明拦截成功 - 注意:该参数不影响 JVM 自身根据内存压力触发的 Full GC,只屏蔽人工/第三方库发起的显式请求
定位具体是谁在调用 System.gc()
拦截只是止痛,定位才是根治。尤其当第三方库(如旧版 JXL、某些图像处理工具包)或内部工具类悄悄调用时,光靠代码 grep 很难发现。
- 推荐用 Arthas 的
stack命令动态追踪:先开启 unsafe 模式(options unsafe true),再执行stack java.lang.System gc - 输出结果会显示完整调用栈,例如
at jxl.write.biff.File.close(File.java:135),直接定位到哪一行代码触发了 GC - 若无法连上生产环境,可临时在测试环境复现,用
jstack+ 关键字搜索辅助缩小范围(如搜System.gc、Runtime.getRuntime().gc())
为什么不能只依赖 DisableExplicitGC?
这个参数虽能立即缓解症状,但掩盖了设计隐患:
- 频繁调用
System.gc()往往意味着对象生命周期管理混乱,比如缓存未及时清理、流未关闭、大数组反复创建等 - 某些场景下(如 JNI 资源释放),调用者可能依赖 GC 的副作用来间接触发清理逻辑,强行禁用可能导致资源泄漏
- 部分老框架或中间件(如早期 Druid 连接池配置)会默认启用显式 GC,需结合文档和版本升级一并治理










