Gradle构建慢主因是配置阶段卡顿而非CPU不足,表现为低CPU高延迟,典型症状为终端静默、%CPU仅10%~20%,验证需用--profile查看「Configuration」阶段占比是否超30%。

Gradle构建时CPU占用低却很慢,大概率卡在配置阶段
这不是CPU不够,而是Gradle在反复解析脚本、计算依赖图、加载插件——这些是单线程、I/O密集型操作,不怎么吃CPU,但特别耗时。你看到top里Java进程只占10%~20%,不代表它没在忙;它可能正卡在读取几十个build.gradle文件、校验settings.gradle里的include路径,或者为每个模块重复执行plugins { id 'com.android.application' }。
验证方法很简单:加--profile跑一次干净构建,打开build/reports/profile/index.html,重点看「Configuration」阶段占比。如果超过30%,基本就是这里拖垮了整条链路。
- 典型症状:终端长时间静默,无日志输出,
ps aux | grep gradle显示进程存在但%CPU极低 - 常见诱因:
apply from:动态脚本过多、subprojects闭包里做耗时逻辑、AGP版本升级后未适配新插件加载方式 - 快速缓解:在
gradle.properties中启用org.gradle.configuration-cache=true(Gradle 7.4+),首次构建稍慢,后续配置阶段可下降60%+
并行开关开了但没效果?检查模块间耦合和configureondemand
org.gradle.parallel=true只对**相互独立的模块**生效。一旦某个模块在build.gradle里写了project(':lib-core').tasks.named('compileJava')这种跨项目引用,Gradle就必须串行化处理——因为要确保:lib-core先完成配置,才能继续下游模块。
更隐蔽的是configureondemand被默认禁用。它能让Gradle只加载当前构建任务实际需要的模块,跳过其他include但未参与本次构建的模块。很多团队开了parallel却忘了开它,等于白配。
- 必须同时启用:
org.gradle.parallel=true+org.gradle.configureondemand=true - 检查是否真独立:运行
./gradlew :app:assembleDebug --dry-run,看输出里有没有大量:other-module:xxx任务——如果有,说明模块边界没切干净 - 注意副作用:
configureondemand与某些老插件(如旧版findbugs)不兼容,开启后若报Unknown project类错误,需排查插件调用位置
Daemon在跑,但每次构建都像冷启动?查--status和IDE集成行为
你以为org.gradle.daemon=true就万事大吉了?其实IDEA/Android Studio默认会用自己的Gradle JVM参数覆盖项目级gradle.properties,导致你配的-Xmx4g根本没生效;或者CI脚本里写了gradle --stop,每轮构建前主动杀掉daemon。
先确认daemon是否真在复用:gradle --status。如果输出里Uptime只有几秒,或Idle时间远小于两次构建间隔,说明它根本没活过上一轮。
- IDEA用户:进
Settings → Build → Build Tools → Gradle,把Build and run using和Run tests using都设为Gradle(而非IntelliJ IDEA),否则IDE会绕过daemon自己起JVM - CI用户:禁止在脚本开头写
gradle --stop或pkill -f gradle;改用gradle --no-daemon显式禁用(仅调试时) - 内存不足表现:daemon存活但频繁GC,
jstat -gc显示FGCT(Full GC次数)飙升,此时要加-XX:MaxMetaspaceSize=512m防元空间爆掉
构建缓存全开却没提速?确认buildCache真正命中
org.gradle.caching=true只是打开了客户端开关,不代表缓存就生效了。Gradle构建缓存分两层:本地磁盘缓存(默认开启)和远程缓存(需额外配置)。如果你只开了caching但没配buildCache { remote(HttpBuildCache) },那缓存只在单机有效——换分支、清build/目录,缓存就废了。
更关键的是,不是所有task都支持缓存。比如androidSourcesJar、generateDebugBuildConfig这类带时间戳或随机值的task,默认被标记为NOT_CACHEABLE。Build Scan报告里的「Cacheable tasks」比例低于70%,缓存收益就会断崖下跌。
- 验证是否命中:执行
./gradlew assembleDebug --scan,打开生成链接,在「Build Cache」页看「Remote cache hits」和「Local cache hits」数字 - 强制缓存敏感task:在对应模块
build.gradle里加tasks.withType(JavaCompile).configureEach { outputs.cacheIf { true } }(慎用,需确保输入完全确定) - 别踩的坑:
buildCache与configuration-cache目前不能共存(Gradle 8.8前),开了后者就得关前者,反之亦然
最常被忽略的一点:配置阶段优化收益远大于任务执行阶段。很多人狂调-Xmx8g、加SSD,却放任30个模块各自apply from: '../gradle/versions.gradle'——每次构建都要重新读、解析、执行同一份脚本30遍。把公共逻辑抽成init.d脚本或预编译插件,比多塞2G内存实在得多。











