系统盘空间释放之 Gradle 的默认缓存迁移
先说说我自己的情况。上个月开发机 C 盘又飘红了,排查一圈发现.gradle这个目录占了 40 多 G。干我们这行的都懂,这玩意儿是 Android 和 Java 工程绕不过去的坎。刚开始以为删了就完事,结果我顺手改了系统环境变量把 Gradle 缓存挪到了 D 盘,又把原来那个 40G 的缓存目录按项目重新缓存预热了一遍。这一套折腾下来,C 盘多出了 35G 可用空间,而且之后新拉的项目再也没出现过“系统盘爆红”的警告。
这篇内容就是把你可能也会遇到的 Gradle 缓存迁移、镜像配置、依赖损坏这几件事讲清楚。适合电脑 C 盘常年告急、公司强制用 Windows 开发、或者刚入坑 Gradle 还没搞懂缓存机制的朋友。我不会给你贴一堆看似专业的术语,只会告诉你我是怎么排查、怎么改、怎么验证的,以及那些踩过之后才明白的坑。
1. Gradle 缓存为什么成了系统盘空间杀手
1.1 GRADLE_USER_HOME 是这一切的源头
Gradle 默认会在当前系统用户目录下创建一个.gradle文件夹,至于这个文件夹具体在哪,取决于GRADLE_USER_HOME环境变量的值,如果不设置,默认就是C:\Users\你的用户名\.gradle。这个目录在 Windows 上几乎必然是 C 盘,在 macOS 上是/Users/你的用户名/.gradle,Linux 则是/root/.gradle或/home/用户名/.gradle。
这里要搞清楚一个概念:Gradle 缓存不是只有依赖包那么简单。GRADLE_USER_HOME下通常有四个大头:caches(依赖 jar 包和元数据)、wrapper(Gradle 发行版压缩包和解压后的完整运行时)、daemon(Gradle 守护进程日志和注册信息)、以及notifications、native之类的小杂项。一个持续开发了半年的项目,caches占掉 8~15G 很正常,如果同时维护三四个项目,daemon日志也能积攒好几个 G,再加上wrapper里存了多个 Gradle 版本,一个版本就 500MB 左右,三四个版本就是 2G 多。
这就带来一个问题:程序本身没多大,但构建系统把运行产物和依赖都塞到了系统盘里。你可以打开系统和用户目录看下,如果C:\Users\你的用户名\.gradle已经膨胀到 10G 以上,那基本可以确定是 Gradle 在系统盘里养成了“全家桶”。
1.2 为什么说它比 Maven 更吃空间
Maven 的本地仓库默认在C:\Users\用户名\.m2\repository,它只是单纯地存 jar 包。但 Gradle 不同,它的缓存策略更激进,会根据依赖的属性和来源分别存储,而且同一个库的不同版本会全部保留,不会自动清理旧版本。再加上 Gradle 还会缓存构建脚本编译产物、Kotlin DSL 脚本编译结果、注解处理器的生成代码,这些统统塞进caches目录。
实际测算下来,同样一个 Spring Boot 项目,Maven 依赖可能占 800MB,Gradle 缓存能到 2GB,翻了一倍不止。更烦的是 Gradle 6.x 以后每个版本都会生成caches\modules-2、caches\transforms-3、caches\jars-9这一堆带版本号的子目录,升级 Gradle 后旧目录不会自动删,直接原地叠罗汉。
1.3 清理 vs 迁移:哪个才是根治方案
很多人遇到 C 盘满的第一反应是手动删除.gradle下的缓存目录。这种办法确实能瞬间释放十多个 G,但代价是下一次构建时 Gradle 需要重新下载所有依赖。要是公司网络快、镜像配置好也就算了,如果是个小水管,那重新拉依赖的时间够你喝三杯咖啡。
所以真正划算的操作是把整个GRADLE_USER_HOME挪到其他盘,让系统盘以后不再接收新产生的缓存数据。迁移之后,就算缓存后续膨胀到 100G,也跟 C 盘没有半毛钱关系。这才是一劳永逸的做法。
2. 迁移方案的整体设计:两种改法怎么选
2.1 环境变量法 vs 软链接法
迁移 Gradle 缓存有两种主流方案:一是改系统环境变量GRADLE_USER_HOME,二是用文件系统软链接。我不推荐新手上来就搞软链接,虽然它看起来省事(把.gradle移到 D 盘,再在 C 盘用户目录下建一个指向 D 盘的符号链接),但 Windows 上创建软链接需要管理员权限,而且有些 IDE 或命令行工具对符号链接的识别并不总是正常。我在公司帮同事弄过好几次,明明链接建好了,但 Android Studio 缓存检查器总报错,排查到最后才发现是链接类型搞错了(目录链接要用/D参数)。
环境变量法就直白得多:把GRADLE_USER_HOME指向D:\GradleUserHome\.gradle,系统里所有调用 Gradle 的程序(命令行、Android Studio、IDEA)都会自动去新目录读写缓存。它的好处是零权限要求,修改后重启 IDE 就生效,而且非常透明,谁都能看懂。
两者对比下来,我的建议是:Windows 上用环境变量,macOS/Linux 上可以用环境变量,也方便用软链接,反正看习惯。核心思路都是让 Gradle 别再往系统盘写数据。
2.2 为什么新目录建议带.gradle子目录
这里有个细节:GRADLE_USER_HOME指向的目录,Gradle 会直接在里面创建caches、wrapper这些子目录。如果你把变量直接设成D:\GradleUserHome,那么下一次构建后 D 盘会出现D:\GradleUserHome\caches、D:\GradleUserHome\wrapper这样的目录结构。
我习惯的方式是设成D:\GradleUserHome\.gradle,这样一眼就能看出来这个目录是 Gradle 的“家目录”,而且后面如果 D 盘还有 Maven 仓库D:\GradleUserHome\.m2,那GradleUserHome根目录下面就是各种构建工具的家目录集合,分区管理特别清晰,找问题也好定位。
2.3 迁移后 Gradle 还能找到旧缓存吗
答案是不能。当GRADLE_USER_HOME改变后,Gradle 会把它当成一个全新的空目录来初始化,之前 C 盘里的所有缓存都会失效。所以迁移之后第一次构建,项目会重新解析所有依赖,本质上等于一次冷启动构建,构建时间会明显变长。
这一点必须在迁移前做好心理预期。想缓解的话,可以把 C 盘旧缓存先完整复制到新目录,这样 Gradle 在新目录里能直接复用原来的缓存内容,省去重新下载的过程。复制大目录在 Windows 上如果你用资源管理器拖拽,速度会非常痛苦,建议用robocopy命令行工具,带多线程参数,几 GB 的目录几分钟就能搬完。
3. 实操过程:Windows 系统完整迁移步骤
3.1 第一步:先看当前 Gradle 家目录到底有多大
管理员身份打开 PowerShell,执行:
# 查看当前 GRADLE_USER_HOME 的值 echo $env:GRADLE_USER_HOME # 查看当前用户 .gradle 目录大小(如果上面输出为空,默认就是这个目录) "{0:N2} GB" -f ((Get-ChildItem -Path "$env:USERPROFILE\.gradle" -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1GB)如果GRADLE_USER_HOME有值,那就以它为准。如果没有值,默认就是C:\Users\你的用户名\.gradle。在我那次折腾中,C 盘的.gradle大小是 42.7GB,当时我一度怀疑统计错了,因为项目才写到一半,后来想想是三个项目和一个 Flutter 工程共享了这套缓存,也就不奇怪了。
3.2 第二步:在目标盘创建新目录
在 D 盘(或空间充裕的分区)新建一个目录,比如D:\GradleUserHome\.gradle。这个目录可以提前建好,也可以不建,后面复制的时候会自动创建,但建议还是手动建一下,确保路径没有权限问题。
注意:目标分区尽量选择固态盘,因为 Gradle 构建过程会大量频繁读写缓存,机械硬盘的速度会成为瓶颈,尤其是首次冷启动时,构建速度可能会掉一半以上。如果 D 盘也是固态,那就没这个问题。
3.3 第三步:复制旧缓存到新目录
PowerShell 里执行 robocopy 命令:
# 把 C 盘旧 .gradle 目录完整复制到 D 盘新目录 robocopy "$env:USERPROFILE\.gradle" "D:\GradleUserHome\.gradle" /E /COPY:DAT /R:1 /W:1 /MT:16参数解释:
/E表示复制所有子目录,包括空目录。/COPY:DAT表示复制数据、属性和时间戳,不复制安全权限,避免权限冲突。/R:1表示文件复制失败时只重试 1 次,默认是 100 万次,遇到权限问题会卡到天荒地老。/W:1表示重试等待 1 秒。/MT:16表示 16 线程并行复制,大目录场景下能明显提速。
robocopy 执行结果里会有个退出码,0~7 都算正常复制,不需要管,只有 8 以上才是异常。
copy完以后,验证一下关键目录是否完整:
Test-Path "D:\GradleUserHome\.gradle\wrapper" Test-Path "D:\GradleUserHome\.gradle\caches"这两个路径存在就说明基本复制成功了。
3.4 第四步:设置 GRADLE_USER_HOME 环境变量
按下Win + R,输入sysdm.cpl回车,在“高级”选项卡里点击“环境变量”。在“用户变量”区域点击“新建”:
- 变量名:
GRADLE_USER_HOME - 变量值:
D:\GradleUserHome\.gradle
这里我建议添加到“用户变量”而不是“系统变量”,因为用户变量不需要管理员权限,而且只对当前用户生效,不会影响系统的其他账号。设置完记得一路点“确定”,然后重启命令行窗口(最好注销并重新登录一次系统,确保所有进程都能拿到新变量)。
重启完后再次验证:
echo $env:GRADLE_USER_HOME如果输出的值是D:\GradleUserHome\.gradle,那环境变量就生效了。
3.5 第五步:验证迁移是否正常
随便找个老项目,在命令行执行:
gradlew clean build --refresh-dependencies首次执行时会看到 Gradle 在新目录里初始化缓存,下载依赖的过程会比较慢。等构建完成,去D:\GradleUserHome\.gradle\caches下面看看,如果出现了依赖 jar 包,说明迁移成功。
如果是 Android Studio 项目,打开 IDE 后会自动触发 Gradle Sync,这个过程中观察日志输出,没有报“Could not find”或“Failed to create cache directory”之类的错误就说明没问题。
3.6 第六步:确认系统盘释放成功
再执行第一步的大小统计命令,C:\Users\你的用户名\.gradle应该已经不存在了,或者只是一个残留的空壳。顺便看看系统盘剩余空间,基本会多出相当于原缓存大小的可用空间。如果原来 C 盘的.gradle在复制完成后已经不想要了,可以手动删掉,注意删的时候如果有程序正在占用(比如 Android Studio 还没关),会提示无法删除。最好完全退出所有 IDE 和 Java 进程后再删。
3.7 macOS / Linux 迁移要点
macOS 和 Linux 操作逻辑完全一样,区别在于目录路径和导出语句。
# 假设旧缓存目录是 /Users/you/.gradle # 1. 创建新目录 mkdir -p /Volumes/Data/GradleUserHome/.gradle # 2. 复制缓存 cp -R /Users/you/.gradle/* /Volumes/Data/GradleUserHome/.gradle/ # 3. 设置环境变量(写入 ~/.zshrc 或 ~/.bashrc) echo 'export GRADLE_USER_HOME=/Volumes/Data/GradleUserHome/.gradle' >> ~/.zshrc # 4. 生效 source ~/.zshrc # 5. 验证 echo $GRADLE_USER_HOMELinux 上如果旧目录是/root/.gradle,复制时注意权限,用sudo cp -R会省很多事。
4. 迁移后的配置优化:镜像加速与缓存策略一起调
4.1 顺手解决 Gradle 下载慢的问题
迁移完成后,第一次构建会触发大量依赖重新下载。如果公司服务器在国外或者网络对 Maven Central 和 Google Maven 访问极慢,那这个首次构建的时间会让人怀疑人生。这也是热词里“gradle下载太慢 android studio”“gradle国内镜像站”反复被搜索的原因。
解决方式是在项目的settings.gradle或build.gradle里配置镜像仓库。现在主流做法是直接配置阿里云镜像或者腾讯云镜像。以settings.gradle为例:
pluginManagement { repositories { maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } maven { url 'https://maven.aliyun.com/repository/google' } maven { url 'https://maven.aliyun.com/repository/central' } maven { url 'https://maven.aliyun.com/repository/public' } gradlePluginPortal() } } dependencyResolutionManagement { repositories { maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://maven.aliyun.com/repository/google' } maven { url 'https://maven.aliyun.com/repository/central' } } }配置完以后,同一份依赖的下载速度会有质的提升。我实测过,同一份 Spring Boot 3.x 依赖,默认源下要 20 分钟(还经常超时),换阿里云镜像后 3 分钟全下完。这个坑强烈建议提前避掉,别等构建到一半突然报SocketTimeoutException才想起来换源。
4.2 设置 daemon 日志不要无限膨胀
Gradle daemon 运行日志默认写在GRADLE_USER_HOME\daemon下,每次构建都会追加输出。时间一长,这个目录也是吃硬盘的隐形大户,一个项目跑 3 个月,日志能到 1~2G。可以通过修改gradle.properties限制日志输出级别:
org.gradle.daemon=true org.gradle.console=plain org.gradle.logging.level=infoinfo级别比debug省不少空间,而且日常排错也够用了。如果你觉得日志根本没用,可以再把daemon目录定期清空,D:\GradleUserHome\.gradle\daemon里的子目录可以放心删,不影响任何项目。
4.3 让 Android Studio 和 IDEA 也认新路径
环境变量设置完毕后,Android Studio 和 IDEA 通常会自动读取系统环境变量。但有一种情况例外:IDE 内置的 Gradle JVM 环境可能和你命令行里用的 JVM 不一样。如果 IDE 里 Gradle 的 JVM 版本和项目要求的 Gradle 版本不匹配,会出现“the project's gradle version 6.7.1 is incompatible with the gradle jvm version”的报错。
这种情况下,迁移只是解决了空间问题,构建兼容性问题还是得另配。操作路径是:File > Settings > Build Tools > Gradle,在Gradle JVM下拉框里选项目要求的 JDK 版本。Gradle 7.x 以上通常要求 JDK 11 或 17,Gradle 6.7.1 则是 JDK 8 或 11,注意对照一下。
5. 常见问题与排查技巧实录
5.1 迁移后构建报错:Could not resolve all dependencies
如果你迁移后立刻在旧项目里执行构建,Gradle 可能因为旧缓存目录的元数据损坏,或者新目录的依赖尚未下载完整,报出各种“Could not resolve”错误。这时候我的排查步骤是:
- 先确认新目录的
caches\modules-2\files-2.1下能不能找到报错库的名称。如果连目录都没有,说明依赖还没下载到,直接换镜像源再构建。 - 如果目录存在但构建仍然报错,大概率是之前的缓存元数据损坏,执行:
gradlew clean然后删掉D:\GradleUserHome\.gradle\caches\modules-2\metadata-*目录,强制 Gradle 重新解析元数据。这个操作是安全的,只是重新生成解析清单,不会删掉实际的 jar 包。
- 最后再构建一次,看看依赖能不能正常解析。
5.2 Gradle 下载发行版一直超时:SocketTimeoutException
这个热词“could not install gradle distribution from 'hreason: java.net.sockettimeoute”对应的场景是:Gradle Wrapper 需要下载某个特定版本的 Gradle 发行包,默认从services.gradle.org下载,网络差的时候必现超时。
解决办法有两个方向。第一,手动下载对应的 Gradle 发行包放到D:\GradleUserHome\.gradle\wrapper\dists下对应目录里;第二,改 Wrapper 的distributionUrl为国内镜像地址。我推荐第二种,可以直接编辑gradle/wrapper/gradle-wrapper.properties:
distributionUrl=https\://mirrors.aliyun.com/gradle/gradle-8.5-bin.zip改完以后重新执行gradlew,Gradle 会去镜像地址下载,速度明显上来了。
5.3 gradlew.bat build 不下载 Gradle,直接卡住
“gradlew.bat build 不下载 gradle”这个热词也很典型。很多情况下不是没下载,而是 Gradle daemon 启动失败后,命令行没有任何提示。检查时先确认 Java 环境变量配好没有:
java -version gradlew --version如果java -version正常但gradlew --version卡住,大概率是 Gradle daemon 在尝试连接之前残留的进程。直接杀掉所有 Gradle 相关进程:
taskkill /F /IM java.exe再重跑。如果还不行,检查D:\GradleUserHome\.gradle\wrapper\dists里对应的压缩包是不是 0KB,这通常意味着下载中断过,删掉那个 0KB 文件重新跑就行。
5.4 缓存损坏:Gradle's dependency cache may be corrupt
热词里“gradle's dependency cache may be corrupt (this sometimes occurs after a netw”对应的就是这种问题:网络波动导致缓存目录里的部分文件写了一半就中断,Gradle 下次构建时检测到不完整记录,直接报缓存损坏。
我的处理策略是:
- 先关闭所有正在运行的 Gradle 构建。
- 打开
D:\GradleUserHome\.gradle\caches\modules-2\files-2.1,找到报错对应的库路径,手动删除该库的完整目录。 - 重新构建,Gradle 会重新下载这个库。
如果损坏的依赖太多,一个个删太费劲,也可以直接执行:
Remove-Item -Recurse -Force "D:\GradleUserHome\.gradle\caches\modules-2"让它整体重建。代价是全部依赖要重新下载,但总比反复报错强。
5.5 Flutter 项目报错:You are applying Flutter's main Gradle plugin imperatively
热词“you are applying flutter's main gradle plugin imperatively using the apply s”来自 Flutter 2.x 迁移到 3.x 后,项目的 build.gradle 仍然使用旧式apply plugin写法的报错。表面看它和缓存迁移没什么关系,但在迁移 Gradle 缓存后,Flutter 项目如果构建失败,很容易被误以为和缓存目录有关。
实际处理方式是在android/settings.gradle里修改插件声明,以 Flutter 新模板为准:
plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "8.1.0" apply false // 其他插件 }然后在android/app/build.gradle顶部改成:
plugins { id "com.android.application" }这样就把旧的命令式插件应用改成了声明式,避开了报错。这个过程和缓存迁移无关,但会因为你刚折腾完 Gradle 而更容易被串联起来排查。
5.6 Android Studio 配置 Gradle 的常见误区
Android Studio 里配置 Gradle 时,有一个很容易忽略的点:IDE 内置的 JVM 和命令行 JAVA_HOME 不一致。迁移缓存后,如果你在命令行里设置了JAVA_HOME指向 JDK 17,但 Android Studio 里 Global Gradle Settings 的 Gradle JVM 还是 JDK 11,那构建时会报版本兼容错误。
我的习惯是让 IDE 和命令行统一 JDK 版本。在 Android Studio 的Settings > Build Tools > Gradle里把 Gradle JVM 选成和JAVA_HOME一样的 JDK,这样就不会出现两边互相打架的问题。
6. 迁移完成后的日常维护建议
缓存迁走之后,不等于一劳永逸。D:\GradleUserHome\.gradle一样会不断膨胀,只是不再祸害系统盘而已。为了避免它哪天把 D 盘也塞满,建议养成两个习惯。
第一个习惯是定期清理旧版本缓存。Gradle 自带清理命令:
gradlew --stop然后手动删除D:\GradleUserHome\.gradle\caches\modules-2\files-2.1下那些超过半年没用的库目录。说白了就是看你最近还构建哪些项目,不用的库删掉没任何影响,最多就是下次构建慢一点。
第二个习惯是控制本机 Gradle 版本数量。很多项目因为老旧原因绑定 Gradle 6.x 或 7.x,每切换一个项目就要下载对应的 Gradle 发行版到wrapper\dists里。检查一下:
Get-ChildItem "D:\GradleUserHome\.gradle\wrapper\dists" -Directory如果发现超过三个版本,且其中有些已经不再使用,把对应目录删掉,能释放好几个 G。我个人一直保留 Gradle 7.6.4 和 8.5 两个版本,兼顾老项目和较新的项目,再老的版本就通过改用 wrapper 动态控制。
第三个建议是,尽量把gradle.properties里的org.gradle.jvmargs合理设置,不要盲目加大内存。有时候堆内存设到 4G,一旦项目一多,daemon 占用的内存会让你的开发机越来越卡。控制台观察一下,如果构建很少发生 OutOfMemoryError,就保持默认 2G 左右即可。
7. 这些坑我都踩过,给你提个醒
迁移 Gradle 缓存这个操作本身不算复杂,但有几个细节不注意到,大概率会白折腾一趟。
第一,环境变量改完,一定要注销或重启,不要只关命令行窗口。很多程序(特别是 IDE 和后台服务)是启动的时候读取环境变量,不会实时刷新。你只改完环境变量不重启 Android Studio,它内部还是按 C 盘的路径去找缓存,然后重建整个C:\Users\你\.gradle目录,你之前复制的 D 盘新目录完全用不上,系统盘也会继续被写占用。
第二,复制旧缓存时别开 Android Studio。如果你开着 IDE 复制caches目录,有些 jar 文件正在被 Gradle daemon 占用,robocopy 可能会报占用错误或者复制不完整。最安全的顺序是:关闭 IDE → 关闭所有 Gradle daemon → 复制 → 设环境变量 → 重启 IDE。
第三,迁移完成后,检查一下 Gradle daemon 状态。执行:
gradlew --status如果还有 daemon 进程跑着旧路径,执行gradlew --stop全部停掉,让它们下次以新路径重新启动。否则可能出现“明明缓存迁移了,但构建时还在访问旧目录”的诡异现象。
第四,如果你是公司网络环境,尽量和运维或者 IT 确认是否配了 Gradle 镜像仓库。如果公司内网有私有 Maven 仓库,记得在settings.gradle里把公司仓库地址放在最前面,避免依赖穿透到公网影响速度。
最后再分享一个小技巧:迁移后如果你用的是 IntelliJ IDEA,可以在Settings > Build Tools > Gradle里把Gradle user home直接指定为D:\GradleUserHome\.gradle,这样就算某些奇葩场景环境变量没生效,IDEA 也会按这个路径走,双保险。Android Studio 同理,在 Global Gradle Settings 里设置即可。实测下来,这个设置项比环境变量优先级更高,能避免很多隐性冲突。