上个月我这边C盘又飘红了,用磁盘分析工具扫了一圈,发现罪魁祸首既不是视频缓存也不是微信聊天记录,而是平时完全没在意过的 Gradle 默认缓存目录。看着那 12.7GB 的占用,我一时不知道该夸 Gradle "尽职尽责",还是该骂它"太能吃"。
其实这个问题的本质很简单:Gradle 默认把缓存放在用户目录下的 .gradle 文件夹里,对 Windows 来说就是 C:\Users\你的用户名\.gradle。这个文件夹会随着你构建次数增加、依赖版本增多、Gradle 发行版切换,像滚雪球一样越滚越大。今天这篇文章就是完整复盘我迁移 Gradle 默认缓存的全过程,包括为什么迁移、迁移前要考虑哪几种方案、每一步的具体操作,以及我在实测中踩过的坑和排查思路。不管你用的是 Android Studio 还是纯命令行 Gradle 构建,这套迁移方法都适用。
1. 为什么不用急着删缓存:先搞清 .gradle 里装了什么
1.1 一个真实的磁盘告急现场
先说我自己遇到的情况。上个月 20 号左右,我在编译一个 Flutter 项目时突然报 "No space left on device",当时我以为是 Docker 镜像占用太多,手动清了一轮镜像后有所缓解,但两天后问题再次出现。
后来我用了一款叫 WizTree 的磁盘分析工具扫整个 C 盘,结果吓一跳:C:\Users\admin\.gradle 这个目录居然占用了 12.7GB。当时我第一反应是"是不是缓存文件损坏了导致体积异常",但点进去看了一圈,发现每个子目录都挺正常,就是单纯的东西多。
1.2 Gradle 默认缓存目录的构成
为了说清楚迁移的必要性,先带你看看 .gradle 目录下到底有哪些"大块头"。用 du -sh 或者 Windows 下的文件夹属性查看,一般会看到这几个重点目录:
| 目录 | 作用 | 典型占用 | 能否手动删 |
|---|---|---|---|
caches/modules-2 |
依赖模块缓存,比如 com.android.tools.build:gradle 等 Maven 依赖的 jar/aar | 通常最大 | 可以删但代价大,删了要重新下载 |
caches/transforms |
AAR 解压和资源转换后的产物 | 较大 | 可以删,会重新生成 |
caches/build-cache-1 |
构建缓存,跨项目共享的 task 输出 | 视项目而定 | 可以删,但会损失增量构建速度 |
wrapper/dists |
各版本 Gradle 发行版的完整压缩包和解压文件 | 较大 | 谨慎删,删除后切版本要重新下 |
daemon |
Gradle 守护进程的日志和注册信息 | 较小 | 可以删,不影响构建 |
jdks |
部分场景下 Gradle 工具链自动下载的 JDK | 视情况而定 | 可删,但建议保留 |
注意看 wrapper/dists 这一项。如果你同时维护多个项目,A 项目用 Gradle 7.4,B 项目用 Gradle 8.0,C 项目升到了 8.5,那这三个版本的发行版都会被完整保留,每个版本解压后动辄 150MB 起步,再加上下载的 zip 包,几个版本叠加起来就是几个 G。
1.3 判断你是否需要做缓存迁移的三个信号
不是所有人都有必要马上迁移,我结合自身和周边同事的情况总结出三个信号:
- 系统盘分区较小:如果你的 C 盘只有 120GB 或 256GB,而
.gradle目录已经超过 8GB,那迟早会出现"磁盘空间不足导致构建失败"的情况。等到报错再处理,还挺被动的。 - 多项目多版本 Gradle 并行:今天拉一个 Flutter 项目,明天跑一个 Android 原生项目,后天又试一个 Kotlin Multiplatform 库,
wrapper/dists和caches/modules-2会急剧膨胀。 - 你经常切换分支或并行开发多个依赖库:由于不同分支的依赖版本不同,Gradle 会保留所有版本而不是只留下当前分支用到的版本,时间一长重复和冗余会非常明显。
如果你命中了两条以上,这篇文章接下来的内容就很适合你。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动手迁移前,先把这三种方案想清楚
2.1 方案一:环境变量直改 GRADLE_USER_HOME
Gradle 设计上本身就支持通过环境变量运行用户构建目录重定向,这个环境变量叫 GRADLE_USER_HOME。默认情况下它指向 ~/.gradle,你改成其他盘符的目标路径后,所有基于 Gradle 的构建、Android Studio 的 Gradle 同步、命令行中的 gradlew 脚本都会默认使用新路径。
这个方案的好处很明显:
- 全局生效,改一次就能管住本机所有项目,不需要逐个项目改配置。
- 切换成本低,你不需要移动任何文件,只要把旧目录里的内容复制到新路径即可(甚至不复制也没问题,就是第一次构建会比较痛苦)。
- 符合官方预期,很多 CI 机器上就是这么配置的。
它唯一的限制是:如果你用的是旧版 Gradle 工具(比如 4.x 时代),某些早期版本对 GRADLE_USER_HOME 的支持不是 100% 稳定,不过现在主流环境基本都是 Gradle 6/7/8,这个顾虑可以直接忽略。
2.2 方案二:目录联接(symlink / junction)
Windows 下有 mklink /J 命令可以创建目录联接,macOS/Linux 下有 ln -s 命令创建软链接。这个方案的本质是"骗过" Gradle,让它以为缓存还在原来的 C:\Users\admin\.gradle,实际操作上目录已经被重定向到 D 盘或 E 盘。
这个方案我试过,优点确实不少:
- 无需修改环境变量,对所有工具链完全透明,不会出现某些 gradle 插件或者 IDE 因为读取不到环境变量而回退到默认目录的情况。
- 系统层面的重定向,甚至不需要重启终端新开会话就能立即生效。
但它的缺点也很致命:如果以后清理系统或重装系统时忘了这个联接,处理起来容易绕晕,有时候还会遇到权限不足的诡异报错,排查路径比较费劲。
2.3 方案三:复制旧目录 + 环境变量(我最终的选择)
我在权衡之后选择的是 复制旧缓存 + 配置 GRADLE_USER_HOME + 保留原目录软删除 的组合。
之所以不选纯软链接,是因为 Windows 下目录联接在某些 IDE 的增量更新场景中出现过文件锁定和同步异常的问题,而环境变量的方式更"直球",出了问题更好排查。另一个重要原因是:Gradle 的缓存文件是相互独立的,你直接复制到新目录后,校验逻辑会通过 metadata 里的 checksum 来发现文件是否完整,不完整会自动重新下载,所以哪怕复制过程中有个别文件损坏也没关系,Gradle 会自愈。
如果你是在 Linux 服务器上操作,同理适用。这里我把方案对比整理成一张表:
| 对比维度 | 环境变量直改 | 目录联接 |
|---|---|---|
| 配置复杂度 | 低 | 中 |
| 对 IDE 的透明性 | 较好 | 极好 |
| 排障成本 | 低 | 中 |
| 重装系统后的后遗症 | 无 | 可能忘掉联接指向 |
| 推荐场景 | 个人开发机、CI、服务器 | 纯命令行环境或不想改全局配置 |
3. 完整迁移操作实录:从关停守护进程到编译验证
3.1 迁移前的一分钟检查清单
那现在我直接把你拉到我的实操现场,一步步走完整个迁移过程。首先要做的是清退所有 Gradle 相关进程,千万别省这一步。
我这里踩过一次教训:之前迁移一半时发现 copy 任务报 "The process cannot access the file because it is being used by another process",查了半天才发现是 Android Studio 的 Gradle Daemon 还在后台运行,锁住了部分缓存文件。
正确的做法是:
- 关闭所有 IDE:Android Studio、IntelliJ IDEA、VS Code 里如果有 Gradle 插件在后台同步,全部退出。
- 停掉 Gradle Daemon:在任意项目目录下执行
./gradlew --stop(Windows 是gradlew.bat --stop),这会优雅终止所有守护进程。 - 确认没有 Java 进程残留:Windows 上可以在任务管理器里找一下
java.exe,Linux 上用ps -ef | grep java,如果还有 java 进程在跑,很有可能是某个 IDE 的后台服务,需要手动结束。
检查清单走完之后,我建议顺手把 C:\Users\admin\.gradle\daemon 目录下的 .log 文件清一遍。这些日志只是守候进程的文字记录,清掉完全不心疼,还能腾出几百 MB 空间,更重要的是防止复制过程中混入正在被写入的日志文件。
3.2 用 robocopy 复制出可靠的新缓存目录
接下来是复制。Windows 用户我推荐使用 robocopy 而不是普通的 Ctrl+C 复制,原因是 robocopy 支持多线程复制、断点续传、日志输出,对大量小文件(缓存目录里几乎全是小文件)的效率远高于图形界面手选复制。macOS/Linux 下用 rsync 也是同样的道理。
以 Windows 为例,假设你的目标路径是 D:\GradleCaches\.gradle,在 CMD 中执行:
cmd复制robocopy "C:\Users\admin\.gradle" "D:\GradleCaches\.gradle" /E /COPY:DAT /R:1 /W:1 /MT:16 /LOG:"D:\gradle_copy.log"
参数说明:
/E:复制所有子目录,包括空目录。/COPY:DAT:复制数据、属性、时间戳,不复制安全权限(后面单独处理)。/R:1和/W:1:失败重试 1 次、等待 1 秒,避免文件锁导致死磕。/MT:16:16 线程并行复制,速度更快。/LOG:把日志输出到文件,方便复制完后排查。
复制过程我实测 12GB 的目录大概用了 8 分钟,中间没有报错。复制完后,通过日志末尾的 "Files: Total ... Copied ... Failed ..." 内容确认没有失败项。你也顺便手动比较一下两个目录的"总大小"和"包含文件数"是否一致——右键属性里能看,如果一致基本说明复制完整。
这里有一个需要留意的操作细节:旧目录 daemon 子目录下的日志文件我建议不要复制,它对新缓存位置没有价值。如果你已经一并复制了,问题也不大,不会影响构建,只是以后清理时多了一层。
3.3 设置 GRADLE_USER_HOME 环境变量
复制完成后,开始配置环境变量。
Windows 上,按下 Win + R 输入 sysdm.cpl 打开系统属性,切到"高级"选项卡,点击"环境变量"。在用户变量区新建一个变量:
code复制变量名:GRADLE_USER_HOME
变量值:D:\GradleCaches\.gradle
这里我建议大概率放在用户变量而不是系统变量。原因比较简单:如果你只是自己开发用,用户级变量足够了,没必要影响系统上所有账户;如果你这台机器是多人共享、或者有 CI 服务在跑,才考虑系统级变量。
设置完之后重新打开一个 CMD 窗口(注意,已经打开的终端不会自动刷新环境变量),执行:
cmd复制echo %GRADLE_USER_HOME%
确认输出是 D:\GradleCaches\.gradle 就说明环境变量已经生效。
macOS/Linux 下,在 ~/.bashrc 或 ~/.zshrc 里追加:
bash复制export GRADLE_USER_HOME=/data/gradle-caches/.gradle
然后 source ~/.bashrc 或者新开终端窗口。
3.4 Android Studio 的联动配置
环境变量配置好之后,还有一步容易漏:如果你和我一样用 Android Studio,需要在 IDE 里同步指定一下新的 Gradle 用户目录。
打开 Android Studio,依次进入 File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle,在右侧的 "Gradle user home"(部分版本叫 "Service directory path")一项中,把默认的 C:\Users\admin\.gradle 改掉,替换成 D:\GradleCaches\.gradle。
这一步很多人会忽略,因为理论上环境变量已经设了,IDE 应该自动读取。但我在实测中发现:Android Studio 在启动时会缓存读取到的 Gradle 设置,如果你不主动改这一项,它仍然可能拿着旧路径去翻缓存,一旦 C:\Users\admin\.gradle 已经不存在了,IDE 会误以为 Gradle 没有配置好,重新去下载发行版和依赖,白白踩坑。
改完后记得点 Apply,然后重启一下 Android Studio 再打开项目。
3.5 构建验证:如何确认缓存真的被"命中"
配置完毕,接下来就是验证。我建议你找一个平时编译依赖较多的项目,在命令行执行一次简单的 Gradle 任务,比如:
bash复制./gradlew help --info
或者 Android 项目直接执行:
bash复制./gradlew :app:assembleDebug --info
执行过程中注意观察日志中类似这样的一段:
code复制Starting a Gradle Daemon, 1 busy Daemon could not be reused, use --status for details
如果日志里没有出现大量的 "Downloading https://..." 字样的请求,说明依赖缓存已经被正确命中,没有重新下载,迁移是成功的。
同时你也可以在另一个终端窗口实时观察新目录的大小变化:
bash复制watch -n 2 du -sh /data/gradle-caches/.gradle
如果新目录大小在构建前后没有明显增长,且旧目录大小保持不变,那就说明所有缓存读写都稳定走新路径了。
3.6 旧目录的安全处置
验证没问题后,别急着删旧目录,先把 C:\Users\admin\.gradle 重命名一下。
Windows 下可以在文件管理器中直接改成 .gradle_old,Linux 用:
bash复制mv ~/.gradle ~/.gradle_old
这样做的目的很朴素:万一之后发现某些工具还是通过绝对路径读取旧目录,还能快速恢复。等稳定运行一周左右,确认所有项目都能正常构建、IDE 没有任何异常,再执行最终删除:
bash复制rm -rf ~/.gradle_old
删除之后 C 盘那 12GB 就彻底释放出来了。
4. 迁移过程中实测最容易翻车的四个坑
4.1 坑一:复制一半遭遇文件占用,导致新目录"缺胳膊少腿"
这个坑我在 3.1 节提到的检查清单中已经做了预防,但还是放在这里单独强调一下。
有一次我图省事,没有关闭 Android Studio 就直接跑 robocopy,结果复制到一半报了一大堆 "The process cannot access the file because it is being used by another process" 的错。更麻烦的是,由于 robocopy 默认遇到失败会重试 3 次,整个复制耗时长了一倍,最后新目录里的文件还不完整。
排查思路:先用 Process Explorer(Windows)或 lsof(Linux)定位是哪个进程打开了这些文件。通常就是 java.exe(Gradle Daemon 进程)和 studio64.exe(Android Studio)。
处理方式:结束所有相关进程后,重新执行一次 robocopy 命令,这次因为源目录文件没有被锁,复制速度会快很多,而且日志末尾的 Failed 数量应该是 0。
4.2 坑二:环境变量明明改了,Gradle 仍往旧目录写
这个现象在 macOS 上比 Windows 更常见。你明明在 ~/.zshrc 里加了 export GRADLE_USER_HOME=/data/gradle-caches/.gradle,执行 echo $GRADLE_USER_HOME 输出的也是新路径,但真正跑 ./gradlew 任务时,日志里的 user home 路径还是老的 ~/.gradle。
根因分析:有两种可能。第一种是 gradlew 脚本被某个 wrapper 属性文件覆盖了配置,在项目的 gradle/wrapper/gradle-wrapper.properties 里有 distributionPath 相关配置,虽然不影响 user home,但有些老项目会额外通过 gradle.properties 里的 org.gradle.user.home 属性强制指定路径,这个优先级高于环境变量。
第二种可能是终端环境变量和作用域的问题:如果你是在 IDE 的 Terminal 面板里执行的命令,而 IDE 启动时没有继承最新的 shell 配置,那 IDE 里打开的终端会话可能读不到新的环境变量。
处理方式:先查项目根目录下的 gradle.properties 里有没有形如 org.gradle.user.home=... 的配置,有就注释掉;然后确认你用的是一个全新打开的终端窗口;最后实在不行就在项目里用 init 脚本兜底,创建一个 init.gradle 文件并加上:
groovy复制allprojects {
buildDir = new File(System.getenv('GRADLE_USER_HOME'), 'build')
}
不过这个方案是下策,正常情况下前面两步就能解决 90% 的问题。
4.3 坑三:Android Studio 同步后仍在旧路径生成 .gradle 子目录
这个坑出现的场景是:你迁移完系统,重新打开 Android Studio,项目开始 Sync,然后你发现 C:\Users\admin\.gradle 又被创建出来了,里面还冒出了 caches、daemon 这些子目录。
根因:新版 Android Studio 的 Gradle 工具链里有几个组件(比如 Kotlin 脚本编译、Compose 编译器)是通过独立进程启动的,它们不一定继承系统环境变量;如果你在 Settings 里没有同步修改 Gradle user home,IDE 会用自己内部的逻辑去默认路径创建目录。
处理方式:回到 3.4 节提到的 Settings 里,把 "Gradle user home" 手动设置成新路径。这里我还想提醒一点:改完之后最好重启一下 IDE,别只点 Apply,实践下来重启能强制刷新所有 Gradle 相关组件。
4.4 坑四:多个 Gradle 版本切换时,wrapper 头文件路径错乱
最后这个坑比较隐蔽,在团队协作场景容易遇到。
如果你原来在旧目录下通过 Gradle wrapper 下载过多个发行版,迁移之后不同项目切换到不同版本时,gradlew 脚本会使用 GRADLE_USER_HOME 指向新路径下的 wrapper/dists。正常情况下,如果复制完整,新目录里有完整的 zip 文件和校验文件,Gradle 只需解压即可。
但如果你迁移过程中只复制了 caches 里的 modules-2 而没有复制 wrapper/dists,那么切换项目后的首次构建会重新下载对应版本的 Gradle 发行版。如果公司内网没有代理,这个下载量能让人怀疑人生。
处理方式:迁移前先检查一下 wrapper/dists 目录的大小,如果超过 2GB,那建议你优先把它完整复制过去,再考虑其他目录。
5. 缓存迁移之后,我顺手做的几个优化
5.1 给 Gradle 换源:阿里云镜像配置
既然缓存都搬到新盘了,网速这一个绕不开的痛点在迁移后也值得一并处理。如果你在构建项目时经常遇到依赖下载超时,多半是访问 Maven Central 或 Google Maven 的网络延迟太高。
国内开发者最常用的换源方式是在项目的 build.gradle(或新版用 settings.gradle)里加上阿里云镜像:
groovy复制pluginManagement {
repositories {
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/central' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
google()
mavenCentral()
}
}
dependencyResolutionManagement {
repositories {
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/central' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
google()
mavenCentral()
}
}
有同事用的是腾讯云镜像,配置方式类似,镜像地址从 maven.aliyun.com 换成 mirrors.cloud.tencent.com 即可。二者实测都挺稳定,你按自己的网络环境选一个就行。
这里要特别提醒:镜像源只是改变了下载源,不改变缓存路径。换源之后,依赖会先落到新盘上的 caches/modules-2 里,后续构建依然不用走网络,这部分体验在迁移后非常顺畅。
5.2 开启构建缓存和本地 Maven 仓库
迁移后我顺便在 gradle.properties 里开启了构建缓存:
properties复制org.gradle.caching=true
org.gradle.parallel=true
org.gradle.daemon=true
org.gradle.caching=true 会让 Gradle 把任务执行结果缓存到 caches/build-cache-1,下次执行相同的 task 时直接复用输出,不用重新编译。对 Android 项目来说,开启构建缓存后,切换分支时能明显感受到编译耗时缩短。
另外,如果你维护多个模块化项目,也可以考虑在本地生成 Maven 仓库(.m2 目录)让多个项目共享依赖。在 build.gradle 里添加:
groovy复制subprojects {
uploadArchives {
repositories {
mavenDeployer {
repository(url: "file://${System.getenv('GRADLE_USER_HOME')}/local-maven-repo")
}
}
}
}
这个玩法不强制,但对重度本地开发的人来说确实省了不少重复构建时间。
5.3 顺手写了个定期清理脚本
迁移完缓存的最后一步,我写了一个简单的清理脚本放在计划任务里,每周自动跑一次。脚本的思路是先删掉 daemon 目录下的旧日志,再清理 wrapper/dists 里超过 60 天没被访问的旧版本 Gradle 发行版。
Windows 下可以写一个 .bat 脚本放到任务计划程序里,大致逻辑如下:
cmd复制set GRADLE_CACHE=D:\GradleCaches\.gradle
forfiles /p %GRADLE_CACHE%\daemon /m *.log /d -7 /c "cmd /c del @path"
forfiles /p %GRADLE_CACHE%\wrapper\dists /m * /d -60 /c "cmd /c rmdir /s /q @path"
macOS/Linux 下配合 find -atime 和 crontab 可以做到同样的效果。清理完建议使用 ./gradlew --stop 保证没有守护进程占用文件,避免删不掉。
最后的小建议
这次迁移之后,我最大的感受是:Gradle 缓存这东西,平时没人注意,但真到了磁盘告急那一天,它带来的"工程量"远超你想象。迁移前花十分钟想清楚方案,迁移中严格执行检查清单,迁移后留出观察期再删旧目录,整个过程没有想象中那么复杂,关键步骤一门心思做好就不会翻车。
如果你现在刚好 C 盘吃紧,又经常被 Gradle 下载依赖卡得怀疑人生,那就别犹豫了,挑个时间给 .gradle 搬个新家。一次折腾,换来的是长期稳定的构建体验。
