Gradle性能调优:让构建飞起来

说实话,Gradle慢起来真的让人抓狂。我见过不少团队,每天光等构建就要浪费半小时。我自己也踩过不少坑,今天就把这些经验分享给你。

Gradle性能调优,说白了就是三件事:让JVM更聪明、让并行更高效、别写烂代码。我们一个一个说。

核心思路:Gradle构建慢,90%的原因是你没给它合适的运行环境,或者脚本写得有问题。调优不是玄学,是有章可循的。

一、JVM参数调优:给Gradle配个好引擎

Gradle跑在JVM上,JVM的参数直接影响构建速度。我习惯在gradle.properties里配置这些参数:

# gradle.properties
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

几个关键参数:

  • -Xmx:最大堆内存。项目越大,这个值要越高。我个人建议至少2GB,大型项目可以给到4GB甚至8GB。
  • -XX:MaxMetaspaceSize:元空间大小。Gradle会加载很多类,这个值太小容易OOM。
  • -XX:+HeapDumpOnOutOfMemoryError:内存溢出时自动dump堆,方便排查问题。

小技巧:如果你不确定该设多大,可以先给4GB,然后观察构建时的内存使用情况。用jstat或者Android Studio的Profiler都能看。

我曾经遇到一个项目,构建总是莫名其妙地卡死。查了半天,发现是-Xmx设得太小,Gradle频繁触发Full GC。调大之后,构建时间直接缩短了40%。

二、守护进程配置:别让Gradle每次都从头来

Gradle守护进程(Daemon)是个好东西。它会在后台保持一个JVM进程,避免每次构建都重新加载类和配置。

默认情况下,守护进程是开启的。但有些团队会把它关掉,理由是「怕占用内存」。嗯,这里要注意:关掉守护进程,每次构建都要多花10-20秒的启动时间

# 开启守护进程(默认就是true)
org.gradle.daemon=true

# 设置守护进程的超时时间(单位:毫秒)
org.gradle.daemon.idletimeout=3600000  # 1小时无操作后自动关闭

注意:如果你在CI/CD环境里构建,建议把超时时间设短一些,比如10分钟。否则构建机上的守护进程会一直占着内存。

我个人习惯在本地开发时保持守护进程常驻。你想想看,一天要构建几十次,每次省10秒,一天就能省下好几分钟。

三、并行编译:让CPU满负荷工作

现在的电脑都是多核的,但Gradle默认是单线程编译。这太浪费了。我们可以开启并行编译:

# gradle.properties
org.gradle.parallel=true
org.gradle.workers.max=4  # 根据你的CPU核心数调整

workers.max建议设为CPU核心数减1。比如你的是8核CPU,就设7。留一个核心给系统和其他应用。

我记得有一次给一个大型项目做优化,开启并行编译后,构建时间从12分钟降到了7分钟。效果立竿见影。

四、文件系统监控:别让Gradle瞎扫描

Gradle需要知道哪些文件变了,才能决定要不要重新编译。默认它会扫描整个项目目录。如果你的项目里有很多不需要监控的文件(比如build/.git/node_modules/),那扫描就会很慢。

解决方案是配置file.watch

# gradle.properties
org.gradle.vfs.watch=true
org.gradle.caching=true

同时,在.gitignore里排除掉不需要监控的目录:

# .gitignore
/build
/.gradle
/local.properties

经验之谈:如果你用的是macOS,文件系统监控默认是开启的。但Windows和Linux上可能需要手动配置。我建议在gradle.properties里显式声明,避免不同环境行为不一致。

五、避免常见性能陷阱

这部分是我最想说的。很多性能问题,其实都是脚本写得不好导致的。

陷阱1:滥用allprojectssubprojects

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

subprojects {
    apply plugin: 'com.android.library'
    android {
        compileSdk 34
    }
}

这样做的问题是:每个子项目都会执行这段配置。如果你的项目有20个模块,那这段代码就要执行20次。而且,有些模块可能根本不需要这些配置。

我建议改成按需配置:

// 只在需要的模块里单独配置
// 或者用条件判断
subprojects { subproject ->
    if (subproject.name.startsWith('library-')) {
        apply plugin: 'com.android.library'
    }
}

陷阱2:在配置阶段执行耗时操作

Gradle的构建分三个阶段:初始化、配置、执行。配置阶段应该只做声明,不要做实际的计算或IO操作。

我曾经见过有人这么写:

// ❌ 错误示范:配置阶段执行网络请求
def version = new URL("http://api.example.com/version").text
android {
    defaultConfig {
        versionName version
    }
}

每次执行gradle tasks都要等网络请求完成,太慢了。正确的做法是:

// ✅ 正确做法:延迟到执行阶段
android {
    defaultConfig {
        versionName project.findProperty("versionName") ?: "1.0.0"
    }
}

陷阱3:不必要的依赖解析

Gradle每次构建都会解析依赖。如果你有很多动态版本(比如1.+),那每次都要去远程仓库查最新版本,非常慢。

我建议:

  • 尽量使用固定版本号,比如1.2.3而不是1.+
  • 开启依赖缓存:org.gradle.caching=true
  • 使用--offline参数离线构建(如果依赖没变的话)

陷阱4:Task配置不当

有些Task默认会执行很多不必要的操作。比如lint,如果你不需要代码检查,可以跳过:

// 在gradle.properties里
android.enableLint=false

或者只在CI环境里执行:

tasks.whenTaskAdded { task ->
    if (task.name == 'lint') {
        task.enabled = false
    }
}

六、知识体系总览

下面这张图总结了Gradle性能调优的核心要点:

Gradle性能调优 JVM参数调优 -Xmx / MaxMetaspaceSize HeapDumpOnOutOfMemoryError 守护进程配置 org.gradle.daemon=true idletimeout设置 并行编译 org.gradle.parallel=true workers.max配置 文件系统监控 org.gradle.vfs.watch=true 排除无关目录 避免常见陷阱 滥用allprojects 配置阶段执行耗时操作 不必要的依赖解析 目标:减少构建时间,提升开发效率

七、实战建议

说了这么多,我总结几条最实用的建议:

  1. 先从gradle.properties入手:配置JVM参数、开启守护进程、开启并行编译。这三步能解决80%的性能问题。
  2. 检查你的build.gradle:看看有没有在配置阶段做耗时操作,有没有滥用allprojects
  3. --profile分析构建:执行gradle build --profile,会生成一份HTML报告,告诉你每个阶段花了多少时间。这是定位性能瓶颈的最好方法。
  4. 定期清理./gradlew clean虽然慢,但有时候能解决一些奇怪的问题。我一般每周清理一次。

最后说一句:性能调优不是一蹴而就的事。你每次改一个参数,跑一次构建,看看效果。慢慢积累,你的构建速度就会越来越快。


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