14、性能优化:配置缓存、构建缓存、并行执行、守护进程调优、避免反模式

说实话,Gradle 构建慢起来,真的让人抓狂。

我记得刚接手一个老项目时,一次干净的构建要跑 8 分钟。每次改一行代码,等编译的时间都够泡杯咖啡了。后来我花了一周时间专门优化构建性能,愣是把时间压到了 2 分钟以内。今天我就把这些压箱底的经验分享给你。

14.1 配置缓存:让配置阶段飞起来

Gradle 的构建分三个阶段:初始化、配置、执行。配置阶段会执行所有 build.gradle 里的代码,哪怕你只改了一个文件。你想想看,这得多浪费?

配置缓存(Configuration Cache)就是来解决这个问题的。它会把配置阶段的计算结果缓存下来,下次构建时直接复用。

核心原理:配置缓存将 build.gradle 的执行结果序列化到磁盘,后续构建跳过配置阶段,直接加载缓存。

启用方式很简单,在 gradle.properties 里加一行:

org.gradle.configuration-cache=true

但要注意,不是所有脚本都兼容。我在项目中遇到过自定义 Task 里直接访问文件系统导致缓存失效的情况。解决办法是把这些操作放到 doLastdoFirst 里。

警告:启用配置缓存后,如果构建脚本里有动态创建 Task 的逻辑,可能会报错。建议先在 CI 上跑一轮测试。

14.2 构建缓存:避免重复劳动

构建缓存(Build Cache)和配置缓存是两码事。它缓存的是 Task 的输出结果。比如你改了 A 模块的代码,B 模块没动,那 B 的编译结果就可以从缓存里拿。

我个人习惯在 gradle.properties 里这样配置:

org.gradle.caching=true
org.gradle.caching.debug=false

远程缓存更适合团队协作。我在公司搭了个本地缓存服务器,配置如下:

// settings.gradle
buildCache {
    local {
        enabled = true
        directory = "${rootDir}/.build-cache"
        removeUnusedEntriesAfterDays = 30
    }
    remote(HttpBuildCache) {
        url = 'https://cache.example.com/cache/'
        push = true
    }
}

小技巧:本地缓存目录建议放在 SSD 上,能明显提升读写速度。我曾经把缓存从 HDD 移到 SSD,构建时间直接缩短了 15%。

14.3 并行执行:让 CPU 满负荷运转

现在的电脑动不动就 8 核 16 线程,但 Gradle 默认只用一个线程。这不是暴殄天物吗?

启用并行执行很简单:

org.gradle.parallel=true

还可以指定线程数:

org.gradle.workers.max=4

不过要注意,并行度不是越高越好。我试过把 workers 设成 16,结果 CPU 跑满了,但磁盘 I/O 成了瓶颈,反而更慢。建议根据项目大小和机器配置来调,一般设成 CPU 核心数的一半到三分之二比较合适。

CPU 核心数 推荐 workers 适用场景
4 2 小型项目
8 4 中型项目
16 8-10 大型项目

14.4 守护进程调优:别让它成为累赘

Gradle 守护进程(Daemon)是个好东西。它会在后台保持一个 JVM 进程,避免每次构建都重新加载。但默认配置不一定适合你的项目。

我建议在 gradle.properties 里调整 JVM 参数:

org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError

这里有几个要点:

  • 堆内存:一般 2GB 起步,大型项目可以给到 4GB
  • 元空间:512MB 通常够用,如果遇到 OutOfMemoryError 再调大
  • 堆转储:加上这个参数,万一 OOM 了还能分析原因

注意:守护进程的 JVM 参数不要设得太大,否则会挤占系统资源。我曾经把 Xmx 设成 8GB,结果 IDE 都卡死了。

查看守护进程状态:

./gradlew --status

如果发现守护进程占用内存异常,可以手动停掉:

./gradlew --stop

14.5 避免反模式:这些坑我替你踩过了

优化构建性能,除了做加法,还要做减法。有些写法看着没问题,实际上拖慢了整个构建。

反模式一:在配置阶段执行耗时操作

我曾经见过有人在 build.gradle 里直接执行网络请求:

// ❌ 错误示范
def version = new URL("http://example.com/version.txt").text

这会导致每次配置阶段都去请求网络。正确的做法是放到 Task 里:

// ✅ 正确做法
task fetchVersion {
    doLast {
        def version = new URL("http://example.com/version.txt").text
        // ...
    }
}

反模式二:滥用 allprojects 和 subprojects

很多人喜欢在根项目的 build.gradle 里这样写:

// ❌ 错误示范
subprojects {
    android {
        compileSdk 34
    }
}

这会让每个子模块都应用这个配置,哪怕它根本不需要。更好的做法是只在需要的模块里单独配置。

反模式三:动态依赖版本

// ❌ 错误示范
implementation 'com.example:library:+'

使用 + 会让 Gradle 每次构建都去检查最新版本,导致缓存失效。我建议锁定版本号:

// ✅ 正确做法
implementation 'com.example:library:1.2.3'

反模式四:不必要的文件监听

在开发时,Gradle 会监听文件变化来触发增量构建。但如果监听了太多文件,反而会拖慢速度。可以在 gradle.properties 里限制:

org.gradle.vfs.watch=false

嗯,这个选项在 CI 环境里一定要关掉,否则会白白浪费资源。

14.6 知识体系总览

下面这张图总结了本章的核心内容,你可以把它当作优化清单来用:

Gradle 构建性能优化知识体系 配置缓存 构建缓存 并行执行 守护进程 避免反模式 启用方式 gradle.properties configuration-cache=true 注意 Task 兼容性 本地 + 远程 settings.gradle 配置 缓存 Task 输出 SSD 加速本地缓存 parallel=true workers.max 调优 CPU 核心数一半 注意 I/O 瓶颈 JVM 参数调优 Xmx 2-4GB MaxMetaspaceSize --status 查看状态 四大反模式 配置阶段耗时 allprojects 滥用 动态版本 文件监听 目标:将构建时间从分钟级降到秒级 优化不是一蹴而就的,建议每次只改一个配置,对比构建时间变化

说实话,性能优化是个持续的过程。不要指望一次配置就能一劳永逸。我建议你每次改完配置后,用 ./gradlew --profile 生成一份报告,看看瓶颈到底在哪。

我的经验:优化构建性能,80% 的收益来自 20% 的改动。先搞定配置缓存和构建缓存,这两个效果最明显。其他的可以慢慢调。

好了,这一章的内容就到这里。记住,构建速度就是开发效率。花点时间优化,绝对值得。


公众号:蓝海资料掘金营,微信deep3321