本地缓存目录配置:默认位置、自定义路径、多项目共享、清理策略

构建缓存的物理存储位置直接决定了磁盘占用、多项目复用能力以及运维成本。Gradle 为本地缓存目录提供了从零配置到精细化管理的完整链路,本章将逐一拆解其默认机制、自定义配置方法、多项目共享方案与清理策略。

3.1 默认缓存目录层级与位置

Gradle 构建缓存(Build Cache)的本地存储与依赖缓存(Dependency Cache)、守护进程(Daemon)等共享 Gradle User Home 目录。在不做任何配置时,缓存存放于:

操作系统 默认路径
Linux / macOS
~/.gradle/caches/build-cache-1
Windows
%USERPROFILE%\.gradle\caches\build-cache-1

目录名中的 build-cache-1 中的数字 1 代表缓存存储格式版本。Gradle 升级后若格式产生不兼容变更,会自动创建 build-cache-2 等新目录,旧版本保留供回滚使用。

验证命令:
# 查看当前 Gradle 用户目录位置
gradle --version | grep "Gradle user home"

# 查看缓存目录大小(Linux/macOS)
du -sh ~/.gradle/caches/build-cache-*

3.2 缓存目录内部结构

进入 build-cache-1 后,缓存条目以哈希值命名的子目录存放,结构如下:

build-cache-1/
├── 3a/
│   └── 3a4f8c2d...ab12
│       ├── output.bin              # 构建产物序列化文件
│       └── metadata.json           # 包含缓存键、任务类型、时间戳
├── b9/
│   └── b9e0a7f1...cd34/
│       ├── output.bin
│       └── metadata.json
└── gc.properties                   # 垃圾回收状态文件
  • 两级哈希目录:前缀目录(如 3a/)避免单个目录下文件过多。
  • output.bin:序列化的任务输出产物。
  • metadata.json:记录缓存条目的来源、创建时间、Gradle 版本等元信息。
  • gc.properties:记录最近一次的垃圾回收 (GC) 时间戳,用于指导清理策略。
调试技巧:若怀疑某个缓存条目损坏,可直接删除对应哈希目录,下次构建会自动从远程拉取或重新生成。

3.3 自定义缓存路径

当默认路径所在磁盘空间不足,或需要将缓存挂载到高速 SSD、内存盘时,可通过以下三种方式自定义:

3.3.1 通过 gradle.properties 全局配置

在 ~/.gradle/gradle.properties 中设置:

org.gradle.cache.tasks=true
org.gradle.cache.local=/data/build-cache

该设置对当前用户的所有 Gradle 项目生效。注意,此属性仅控制 构建缓存 位置,并不影响依赖缓存路径。

3.3.2 通过系统属性或环境变量

在构建命令中临时指定:

# 系统属性方式
./gradlew build -Dorg.gradle.cache.local=/tmp/gradle-cache

# 环境变量方式(覆盖优先级最高)
export GRADLE_CACHE_LOCAL=/fast-ssd/cache
./gradlew build

系统属性优先级高于 gradle.properties,环境变量又高于系统属性。具体优先级链为:

  1. 命令行 -D 参数
  2. 环境变量 GRADLE_CACHE_LOCAL
  3. 项目级 gradle.properties
  4. 用户级 ~/.gradle/gradle.properties
  5. Gradle 默认值 (~/.gradle/caches/build-cache-1)

3.3.3 在 settings.gradle 中编程指定

适用于需要根据不同 CI 环境动态切换路径的场景:

// settings.gradle.kts
buildCache {
    local {
        isEnabled = true
        directory = File("/mnt/nvme/build-cache")
        // 还可以同时配置 push/pull 策略
        isPush = System.getenv("CI") == null  // 本地构建 push,CI 仅 pull
    }
}

Kotlin DSL 与 Groovy DSL 均可使用,推荐在 CI 场景下用此方式保证路径随环境变量变化。

常见错误:自定义路径的父目录如果不存在,Gradle 不会自动创建。需要确保目录已存在且具有读写权限。否则会抛出:
Could not create local cache directory /custom/path/build-cache-1 ... (Permission denied)

3.4 多项目共享本地缓存

当多个独立项目在同一台机器上构建时,若它们使用了完全相同的依赖和任务配置,可以通过共享同一个本地缓存目录来加速构建。配置方式如下:

3.4.1 共享模式原理

  • 所有项目设置 directory 指向同一个路径。
  • 缓存条目的键由任务类型、输入文件的哈希、输出文件名等决定——与项目名称无关。
  • Git 分支切换或模块重组不会使共享缓存失效,只要输出内容相同即可命中。

3.4.2 实现共享配置

在每项目的 settings.gradle.kts 中统一指定:

// 项目 A 与项目 B 均配置
buildCache {
    local {
        directory = File("/team-shared-cache/gradle-build-cache")
        isPush = true     // 允许将当前项目的缓存写入共享目录
        isPull = true     // 允许从共享目录读取缓存
    }
}

3.4.3 共享注意事项

场景 建议
CI 多 Agent 同机 设置 isPush = true 仅在一个 Agent 上启用,避免写入冲突。实际用只读挂载更安全。
本地开发机共享 确保所有项目使用相同的 Gradle 版本,不同版本的缓存格式不兼容。
缓存磁盘配额 在共享目录上设置磁盘配额(Linux quota / Windows 磁盘配额),防止缓存膨胀占满系统盘。
并发写入 Gradle 使用文件锁机制,但极端并发时可能导致部分丢失。建议共享目录仅用于 读取,写入分散到各自临时目录。
CI 最佳实践:在 Jenkins/GitLab CI 中,为每个 Node 挂载 NFS 或 EFS 共享缓存目录,通过 GRADLE_CACHE_LOCAL 环境变量注入路径,避免修改项目配置文件。

3.5 清理策略与实战

Gradle 本身不提供自动定时清理本地缓存的机制,需要开发者根据使用情况实施清理。以下是三种常用策略:

3.5.1 Gradle 自带的 GC(Garbage Collection)

Gradle 会在构建结束时检查 gc.properties 中的时间戳,若距离上次 GC 超过 24 小时,会触发一次 弱引用清理——只删除那些不再被任何 Gradle 守护进程引用的缓存文件。此方式不会检查缓存时效或大小。

3.5.2 手动定期清理

通过 cron 任务或 CI 构建前的脚本,按时间或大小阈值清理:

#!/bin/bash
# 清理 7 天前未被访问的缓存条目
CACHE_DIR=~/.gradle/caches/build-cache-1
find "$CACHE_DIR" -type d -name "??" -exec sh -c '
    for dir in "$1"/*; do
        if [ -f "$dir"/output.bin ]; then
            last_access=$(stat -c %X "$dir"/output.bin)
            now=$(date +%s)
            # 7 天 = 604800 秒
            if [ $((now - last_access)) -gt 604800 ]; then
                rm -rf "$dir"
            fi
        fi
    done
' _ {} \;

3.5.3 使用 Gradle Enterprise 或第三方插件

若使用 Gradle Enterprise,可通过其缓存管理界面配置 TTL(Time To Live)和大小上限。社区插件如 gradle-cache-cleanup 也提供了按大小、按时间、按匹配模式清除的能力:

// build.gradle.kts
plugins {
    id("com.github.ben-manes.gradle-versions") version "0.51.0"
}

tasks.register("cleanBuildCache") {
    doLast {
        project.gradle.getGradleUserHomeDir()
            .resolve("caches/build-cache-1")
            .listFiles()?.forEach { prefixDir ->
                prefixDir.listFiles()?.forEach { cacheEntry ->
                    if (cacheEntry.isDirectory) {
                        // 保留最近 3 天内的
                        if (System.currentTimeMillis() - cacheEntry.lastModified() > 3 * 86400_000L) {
                            cacheEntry.deleteRecursively()
                        }
                    }
                }
            }
    }
}
安全清理建议:直接删除 build-cache-1 目录是安全的,Gradle 会在下次构建时自动重建。但更推荐按访问时间删除单个条目,避免清空后首次构建无缓存命中。

3.6 性能优化:缓存目录与存储介质

  • SSD 优先:将缓存目录放在 NVMe SSD 或 RAM Disk 上,可减少 30%~50% 的缓存写入/读取延迟。
  • 避免跨网络文件系统:NFS/SMB 等网络挂载会增加 I/O 延迟,仅适用于只读共享场景。读写时建议用本地盘。
  • 目录大小监控:在生产环境设置监控,当 build-cache-1 目录超过 10GB 时发送告警。
  • 与 CI 工作目录隔离:CI 中应显式指定缓存路径,不要放在项目 checkout 目录内,防止清理 workspace 时误删缓存。

3.7 本章小结

本地缓存目录是 Gradle 构建缓存能力的基石。通过理解默认路径结构、灵活运用三种自定义方式、合理规划多项目共享方案以及实施有效的清理策略,可以在享受构建加速的同时,避免磁盘空间失控。实际生产环境中,建议将目录配置与 CI/CD 系统变量绑定,并通过定期清理脚本保持缓存处于健康状态。