Gradle 构建频繁GC的根源是配置阶段大量创建对象,而非GC参数不当;需通过GC日志确认现象、禁用插件、检查init.gradle及build.gradle中的高分配代码(如ext.versionMap = [:])定位热点。

Gradle 构建时频繁 GC,本质不是“GC 参数没调好”,而是构建过程本身在疯狂创建对象——尤其是配置阶段执行大量 Groovy 脚本、解析依赖、生成任务图时,容易触发 Young GC 飙升甚至周期性 Full GC。直接调 -Xmx 只是掩盖症状,必须定位到具体分配热点。
看 GC 日志:确认是不是真频繁,而不是误报
先验证现象是否真实存在,避免被监控毛刺误导:
- 在 IDEA 的
Help → Edit Custom VM Options里追加:-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:./idea_gc.log,重启后跑一次./gradlew build -x test - 检查日志里
GC pause出现频率:如果Young GC间隔 Full GC 出现在构建中段(非启动/退出时),基本可锁定为构建脚本或插件导致 - 注意区分:IDEA 自身 JVM 的 GC 和 Gradle Daemon 的 GC 是两回事。你要看的是 Daemon 进程日志,不是 IDEA 的
idea.log
用 JFR 录制构建过程:找到谁在疯狂 new 对象
GC 频繁的根因几乎总在对象分配速率(allocation rate)过高,JFR 是唯一能低开销抓到现场的工具:
- 执行:
./gradlew build -x test -Dorg.gradle.jvmargs="-XX:+FlightRecorder -XX:StartFlightRecording=duration=120s,filename=build.jfr,settings=profile" - 打开
build.jfr,重点看:Allocation in new TLAB 和 GC Configuration 标签页 - 按包名排序分配量,90% 的问题集中在:
org.gradle(脚本解析)、org.jetbrains.plugins.gradle(IDEA 插件桥接)、com.android.build(AGP)、以及你项目里自定义的buildSrc或init.gradle - 特别留意
String.concat、StringBuilder.toString、new HashMap在循环中高频出现——这些常藏在自定义插件或动态配置逻辑里
检查 gradle.properties 和构建脚本里的高危配置
很多“优化配置”反而会加剧 GC 压力,尤其在旧版 AGP 或 Gradle 上:
-
org.gradle.configureondemand=true在模块依赖混乱时会导致反复解析,增加元空间压力;建议只在纯 Java 多模块项目中启用,Android 项目慎用 -
org.gradle.unsafe.configuration-cache=true若插件不兼容(如老版本com.google.dagger:dagger-compiler),会在配置阶段反复重建 Task 实例,引发对象爆炸 - 在
allprojects或subprojects闭包里写println、new Date()、System.getenv()等副作用代码,每次配置都会执行并生成新对象 - 使用
fileTree或new File(...)构造路径而非layout.projectDirectory.file(...),会绕过 Gradle 的缓存机制,强制重新计算
快速验证是否由插件或 buildSrc 引起
把嫌疑范围快速收窄到具体组件:
- 临时注释掉
buildSrc目录,再构建;如果 GC 恢复正常,说明你写的 Kotlin 插件或gradle.properties加载逻辑有问题 - 对 Android 项目,执行:
./gradlew --dry-run assembleDebug,观察配置阶段耗时占比。若 >40%,且Configuration on demand关闭时仍高,大概率是某个模块的build.gradle里有dependencies { implementation fileTree(...) }这类动态扫描 - 禁用所有第三方插件(保留
com.android.application和org.jetbrains.kotlin.jvm),逐个开启,配合 JFR 观察分配峰值变化 - 检查是否有自定义的
init.gradle,它会在每个构建中无条件加载——哪怕只是打印一行日志,也会在每次./gradlew调用时 new 出一堆对象
真正卡住人的从来不是 GC 参数怎么设,而是哪行 Groovy 在构建开始前就默默 new 了上万次 LinkedHashMap。JFR 报告里那个 87MB/min 的分配热点,往往就藏在 build.gradle 第 42 行一个看似无害的 ext.versionMap = [:] 里。


















