第 2 章 构建缓存与本地缓存:.buildCache 目录结构、Gradle 缓存路径配置、缓存目录权限问题
2.1 Gradle 缓存机制与 .buildCache 目录概述
Gradle 的缓存体系分为 本地构建缓存(Local Build Cache)和 远程构建缓存(Remote Build Cache)。本章聚焦于本地缓存,其物理载体为项目根目录下的 .gradle/build-cache 文件夹(Gradle 6.0+ 默认启用)。每次任务输出命中缓存时,Gradle 直接从该目录恢复产物,跳过任务执行,从而显著缩短增量构建时间。
缓存内容以散列键(Hash Key)为索引存储,每条缓存记录包含任务输入快照、输出文件及其元数据。目录权限直接影响多用户环境下的缓存共享与命中率。
2.2 默认缓存目录结构与子目录含义
Gradle 6.0+ 的本地构建缓存默认存储在 ~/.gradle/caches/build-cache-1/(旧版本为 build-cache)。其内部结构如下:
| 子目录 / 文件 | 用途 | 典型生命周期 |
|---|---|---|
build-cache-1/<hash>/ | 存储单个任务输出的压缩存档(.tar 格式)及元数据 | 根据 LRU 策略或容量上限清除 |
cache.properties | 本地缓存配置(cacheSize、removeUnusedEntriesAfterDays) | 配置文件,手动修改后生效 |
cachedir.lock | 文件锁,防止多进程同时操作缓存目录 | 进程持有期间存在 |
trash/ | 临时存放即将删除的缓存条目,重启时清理(Gradle 8.0+) | 构建结束后清理 |
2.3 缓存路径配置方法
Gradle 提供三层配置机制,优先级由低到高:settings.gradle 配置脚本、系统属性、环境变量。
2.3.1 settings.gradle 配置(推荐)
// settings.gradle.kts
buildCache {
local {
isEnabled = true
// 自定义目录路径
directory = File(rootDir, ".gradle/my-build-cache")
// 容量上限,单位 MB
cacheSizeMB = 2048
// 超过 7 天未使用的条目自动清除
removeUnusedEntriesAfterDays = 7
}
}
2.3.2 系统属性与命令行参数
# 指定缓存目录
./gradlew build -Dorg.gradle.cache.local.directory=/opt/build-cache
# 禁用本地缓存(临时)
./gradlew build -Dorg.gradle.caching.local.enabled=false
2.3.3 环境变量(适用于 CI/CD)
# Linux/Mac
export GRADLE_USER_HOME=/data/gradle
export GRADLE_CACHE_LOCAL_DIR=/mnt/ssd/build-cache
# Windows PowerShell
$env:GRADLE_USER_HOME="D:\GradleCache"
$env:GRADLE_CACHE_LOCAL_DIR="E:\SsdBuildCache"
注意:
GRADLE_USER_HOME 会同时影响依赖缓存、wrapper 和构建缓存。若仅需改变缓存目录,优先使用 directory 配置项,而非修改用户根目录。2.4 缓存目录权限问题
在共享服务器或多用户 CI 环境下,缓存目录的权限配置不当会导致缓存写入失败或缓存未命中。核心原则:所有运行构建的用户/进程必须对缓存目录具有 rwx 权限。
- 问题场景 1:Permission Denied
当构建任务尝试写入~/.gradle/caches/build-cache-1,但当前用户无写权限时,Gradle 抛出java.io.FileNotFoundException(权限被拒绝)。
解决方案:
# 创建共享缓存组 sudo groupadd gradle-cache sudo usermod -aG gradle-cache $USER # 设置目录组权限为 rwx sudo chgrp -R gradle-cache /opt/shared-build-cache sudo chmod -R g+rwxs /opt/shared-build-cache - 问题场景 2:umask 导致文件权限不一致
若 CI 进程 umask 为 0077,新创建的缓存目录仅对当前用户可读写,其他用户无法读取,导致后续构建缓存未命中(实际内容相同但无法读取)。
修复方式:在构建脚本中添加 umask 设置:
// build.gradle.kts tasks.register<DefaultTask>("ensureCachePermissions") { doLast { val cacheDir = gradle.gradleUserHomeDir.resolve("caches").resolve("build-cache-1") cacheDir.mkdirs() cacheDir.setReadable(true, false) // 全局可读 cacheDir.setWritable(true, false) // 全局可写 cacheDir.setExecutable(true, false) } } - 问题场景 3:根用户 vs 普通用户
Docker 容器内以 root 运行构建,缓存目录属于 root;之后以普通用户身份运行构建时会读取失败。推荐容器内创建非 root 用户并提前 chown:
RUN useradd -m jenkins && \ mkdir -p /cache && \ chown -R jenkins:jenkins /cache USER jenkins
2.5 调试与验证缓存目录状态
Gradle 提供以下调试手段:
- 启用 info 级别日志
输出示例:./gradlew build --info | grep -E "(Build cache|build-cache)"
Storing build cache entry at ~/.gradle/caches/build-cache-1/a1b2c3d4.tar Build cache entry for task :compileJava stored - 命令行查看缓存目录
ls -la ~/.gradle/caches/build-cache-1/ # 查看单个缓存文件元数据 tar -tf ~/.gradle/caches/build-cache-1/a1b2c3d4.tar - Gradle 缓存清理工具
gradle --stop # 停止所有守护进程 gradle --gc # 手动触发垃圾回收和缓存清理
调试技巧:若怀疑缓存未命中由权限导致,可临时将缓存目录权限设为 777 测试。定位问题后务必恢复最小权限原则(755 或 775)。
2.6 常见错误与排查
| 错误信息 | 根因 | 解决方法 |
|---|---|---|
Failed to create directory ~/.gradle/caches/build-cache-1 |
父目录无写权限或磁盘已满 | 检查磁盘空间,修改目录所有权或增加容量 |
Build cache entry from local storage was rejected |
缓存文件校验和失败(可能被其他用户部分覆盖) | 使用文件锁或专用缓存目录;删除损坏条目 |
java.nio.file.AccessDeniedException: cachedir.lock |
多个 Gradle 守护进程或同一用户多次构建竞争锁 | 确保 --no-daemon 或等待锁释放;删除残留 .lock 文件 |
2.7 性能优化建议
- 使用 SSD 或 tmpfs 挂载缓存目录
将GRADLE_CACHE_LOCAL_DIR指向内存文件系统,大幅减少磁盘 I/O:
# Linux tmpfs(占内存,建议 2-4GB) sudo mount -t tmpfs -o size=4G tmpfs /mnt/build-cache # 在 settings.gradle 中指定 buildCache { local { directory = File("/mnt/build-cache") } } - 调整缓存容量与存活时间
默认cacheSizeMB=1024(1GB),removeUnusedEntriesAfterDays=7。对于大型项目,可增至 4GB 并延长存活周期:
buildCache { local { cacheSizeMB = 4096 removeUnusedEntriesAfterDays = 30 } } - 避免缓存目录 NFS 挂载
NFS 延迟高且锁机制不完善,极易导致缓存损坏。优先本地 SSD 或分布式缓存服务(如 S3 后端)。 - CI 场景定期清理
在 CI 流水线中增加定时任务删除超过 30 天未访问的缓存:
find /cache/build-cache-1 -type d -atime +30 -exec rm -rf {} +
注意事项:修改缓存目录权限时,需同时考虑
cachedir.lock 文件锁的访问。若将目录设为 777,锁文件可能被恶意进程占用,建议使用组权限(775)配合同一个用户组运行所有构建进程。通过本章学习,你已经掌握本地构建缓存的磁盘结构、配置路径、权限管理及常见故障排查。下一部分将深入远程缓存协议与云端存储策略。