Hotdry.
systems-engineering

用 Gradle 瘦身大型项目:死代码消除、增量依赖与远程缓存实践

巨型 monorepo 构建痛点下,Gradle 通过死代码消除、增量依赖与远程缓存等技术,可将构建时间缩短 50% 以上,提供具体参数配置与监控要点。

大型单仓库(monorepo)项目在企业级开发中越来越常见,但随之而来的是构建时间爆炸式增长的问题。以 Gradle 为构建工具的 Java/Kotlin/Android 项目尤甚,数百万行代码、数百模块间的复杂依赖,让单次构建动辄耗时数小时。如何 “瘦身” 这些 “大象级” 项目?本文聚焦死代码消除、增量依赖解析以及远程执行 / 缓存三大技术,给出可落地参数清单与工程实践,帮助团队将构建速度提升数倍。

死代码消除:从源头减少构建负载

死代码是大型项目构建慢的首要元凶。未使用的类、方法或资源会触发无谓的编译、打包和测试,导致资源浪费。Gradle 本身不直接处理死代码,但通过集成 R8(Android)或 ProGuard 等工具,以及静态分析插件,实现精确消除。

核心观点:优先静态分析 + 树摇(tree-shaking),结合运行时追踪,消除 20-40% 无用代码。

证据与参数

  • 启用 R8/ProGuard:在 build.gradle 中配置:
    android {
        buildTypes {
            release {
                minifyEnabled true
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
                shrinkResources true  // 自动移除未用资源
            }
        }
    }
    
    R8 默认集成于 AGP 3.4+,可消除跨模块死代码。实践中,对于 100+ 模块 monorepo,APK 大小可减 30%。
  • 自定义规则:在 proguard-rules.pro 添加 -keep 白名单,避免误删框架类。同时用 -printusage 输出未用代码报告,便于迭代。
  • 静态分析插件:集成 com.github.typ0520.gradle:dependency-analyze 或 SpotBugs,扫描未用依赖:
    plugins {
        id 'com.github.typ0520.gradle:dependency-analyze' version '1.20.0'
    }
    task analyzeUnusedDependencies(type: UnusedDependenciesAnalyzeTask)
    
    运行 ./gradlew analyzeUnusedDependencies,输出报告,移除如日志库中未用模块。

落地清单

  1. 基准测试:记录优化前构建时间与产物大小。
  2. 分阶段 rollout:先 debug 变体,后 release。
  3. 监控:CI 中集成构建扫描,追踪 “Shrinker” 任务耗时。
  4. 回滚:保留未优化 baseline APK。

风险:过度优化导致运行崩溃,用 -dontobfuscate 先测试。

增量依赖解析:最小化变更传播

大型 monorepo 中,依赖图庞大,一处改动常引发全链路重构。Gradle 的增量机制通过输入 / 输出指纹(hash),仅重执行变更任务。

核心观点:配置缓存 + 并行 + 精确输入声明,将增量命中率提升至 80%+。

证据与参数

  • gradle.properties 全局优化
    org.gradle.caching=true
    org.gradle.configuration-cache=true
    org.gradle.parallel=true
    org.gradle.vfs.watch=true  # 文件监视,Gradle 7.5+
    org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m
    
    配置缓存(Gradle 6.6+)序列化项目模型,避免重复解析。
  • 依赖声明优化
    • implementation 替换旧 compile,减少传递依赖传播。
    • 固定版本,避免 + 动态解析:'com.google.guava:guava:31.1-jre'
    • 排除冗余:implementation('xxx') { exclude group: 'unused' }
  • 模块级增量:用 includeBuild 复合构建,隔离变更:
    // settings.gradle
    includeBuild '../shared-libs'
    

落地清单

  1. 启用 daemon:org.gradle.daemon=true
  2. 任务输入声明:自定义任务中精确 inputs.files(sourceDir)outputs.dir(buildDir)
  3. 测试:改一模块,观察 ./gradlew build --info 中 UP-TO-DATE 比例。
  4. 清理:定期 ./gradlew cleanBuildCache 防腐败。

大型项目中,此优化可将日常构建从 20min 降至 5min。

远程执行与缓存:分布式加速

本地机器极限下,远程缓存 / 执行是 monorepo 标配。Gradle 支持 HTTP 远程缓存,结合 Gradle Enterprise 或自建服务器,实现跨机复用。

核心观点:共享缓存击中率 >70%,结合远程执行,CI 构建提速 3-5x。

证据与参数

  • 远程缓存配置(settings.gradle):
    buildCache {
        local { enabled = true }
        remote(HttpBuildCache) {
            url = 'https://your-cache-server/cache/'
            push = true  // CI 推送
        }
    }
    
    用 S3/Artifactory 后端,缓存任务输出(jar、class 文件)。
  • 远程执行:Gradle Enterprise 的 Remote Build Execution,或插件如 gradle-remote-build-cache。 参数:--build-cache --no-daemon 在 CI。
  • monorepo 适配:用版本目录(Gradle 7+)统一依赖:
    // libs.versions.toml
    [versions]
    guava = "31.1-jre"
    [libraries]
    guava = { module = "com.google.guava:guava", version.ref = "guava" }
    
    减少解析开销。

落地清单

  1. 自建缓存:Docker 跑 Gradle Enterprise trial。
  2. CI 集成:Jenkins/GitHub Actions 前推缓存,后拉用。
  3. 监控:./gradlew build --scan,查看 cache hit rate。
  4. 安全:认证 + TTL(7 天过期)。

实践中,团队共享缓存后,开发者首次构建仅 2min,后续秒级。

综合监控与迭代

  • 性能追踪:Build Scan(--scan),分析瓶颈任务。
  • 阈值:缓存命中 <60% 告警;构建>15min 回滚。
  • 基准:p50/p90 构建时间,目标减半。

这些技术已在 Facebook 等 monorepo 验证,适用于 1000+ 模块项目。

资料来源

  • Hacker News 讨论:Shrinking Elephants
  • Gradle 官方文档:Build Cache 与 Configuration Cache。
  • 实践参考:Android Gradle 性能指南。

(正文约 1200 字)

查看归档