WSL2 实际只用 40G,为什么 Windows 上却占了 256G?
Linux 里 df -h 显示只用了 40G,Windows 上的 ext4.vhdx 却占了 256G。这个差异很常见,原因是 WSL2 的虚拟磁盘只会自动变大,不会因为删文件就自动变小。下面记录几种回收空间的方法。
问题的现象
先看一个例子:
$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/sdb 1.0T 40G 960G 4% /Linux 里明明只用了 40G,但打开 Windows 的资源管理器或者 Get-ChildItem 一看,ext4.vhdx 这个文件已经膨胀到了 256G。
df 统计的是文件系统当前使用了多少空间,Windows 看到的则是 vhdx 文件已经扩展到多大,两者不是一回事。
原因:虚拟磁盘只增不减
WSL2 并不是把文件直接写在 Windows 的 NTFS 上,而是用一块 ext4 格式的虚拟硬盘(VHDX)来承载整个 Linux 根文件系统。这块虚拟磁盘在 Windows 上就是一个普通的 ext4.vhdx 文件。
它大致是这样工作的:
- 按需增长:你在 Linux 里写入数据,vhdx 文件跟着变大;
- 不会自动收缩:你删掉 Linux 里的文件,ext4 只是把对应的块标记为“空闲”,vhdx 文件本身纹丝不动;
- 上限很高:新版 WSL 的虚拟磁盘上限可以达到 1TB(更早的版本通常是 512GB 或 256GB),所以
df -h里显示的Size很大,并不代表真的占用了这么多空间。
所以,40G 是当前文件系统里的数据量,256G 则是 vhdx 曾经扩展到的大小。
Docker Desktop 也经常占掉很大一块空间。它会单独使用一个虚拟磁盘(新版一般是 %LOCALAPPDATA%\Docker\wsl\disk\docker_data.vhdx,旧版是 %LOCALAPPDATA%\Docker\wsl\data\ext4.vhdx)。可以先把所有 vhdx 找出来,看看究竟是谁占空间:
Get-ChildItem -Path $env:LOCALAPPDATA -Recurse -Filter *.vhdx -ErrorAction SilentlyContinue |
Select-Object FullName, @{N='SizeGB';E={[math]::Round($_.Length/1GB,1)}}这段脚本只扫描 %LOCALAPPDATA%。如果你曾用 wsl --import 或迁移工具把发行版放到了其他目录,需要到对应目录下自行查看。
方案一:稀疏 VHD(目前官方已禁用)
理想情况下,可以开启稀疏 VHD(sparse VHD)。这样 vhdx 删除数据后有机会把空间还给 Windows,不需要每次手动压缩。
wsl --shutdown
wsl --manage <发行版名> --set-sparse true不过现在直接执行通常会遇到下面的报错:
由于潜在的数据损坏,目前已禁用稀疏 VHD 支持。
要强制发行版使用稀疏 VHD,请运行:
wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe
错误代码: Wsl/Service/E_INVALIDARG微软后来因为数据损坏和丢失的反馈,关闭了这个功能,只留下 --allow-unsafe 作为强制开启的选项。
不建议加 --allow-unsafe。 为了省几十 GB 空间,没必要拿整个 Linux 环境冒险。
方案二:fstrim + compact vdisk(推荐)
一般先用这个办法,分两步操作。
先在 WSL 里执行:
sudo fstrim -avfstrim 会把 ext4 中已经空闲的块通知给虚拟磁盘。少了这一步,后面的压缩通常回收不了那么多空间。
然后关闭 WSL,用管理员权限打开命令提示符,进入 diskpart:
wsl --shutdowndiskpart
select vdisk file="C:\完整路径\ext4.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exitcompact vdisk 会尝试回收未使用的块。能压回多少取决于文件系统中空闲块的分布,不一定正好回到 40G。
找到 vhdx 路径的方法:wsl --list --verbose 查看发行版名,默认安装的商店版 Ubuntu 一般在:
%LOCALAPPDATA%\Packages\CanonicalGroupLimited...\LocalState\ext4.vhdx如果你曾经迁移过 WSL,则在自定义目录下。
方案三:Optimize-VHD(需要 Hyper-V)
如果机器上启用了 Hyper-V 功能,可以直接用 PowerShell 一条命令搞定:
wsl --shutdown
Optimize-VHD -Path "C:\完整路径\ext4.vhdx" -Mode Full效果和 compact vdisk 类似,但依赖 Hyper-V 组件(Optimize-VHD 属于 Hyper-V PowerShell 模块)。Windows 家庭版不支持 Hyper-V,无法使用这个方法,请改用方案二。
方案四:导出再导入(压缩最彻底)
如果 compact 效果不理想,或者 vhdx 已经出现了难以处理的异常,可以用导出/导入的方式重建一个只包含实际数据的新虚拟磁盘:
wsl --shutdown
wsl --export Ubuntu D:\backup\ubuntu.tar
wsl --unregister Ubuntu
wsl --import Ubuntu D:\WSL\Ubuntu D:\backup\ubuntu.tar新生成的 vhdx 会紧贴实际数据量,通常是最“干净”的结果。
几个注意点:
- 预留足够的磁盘空间:导出的 tar 大小约等于实际数据量(本例约 40G),存放 tar 的盘要有足够空间;
- 必须先确认 tar 导出成功(检查文件大小是否合理)再执行
--unregister,因为该命令会直接删除原 vhdx,删了就没法后悔了; - 不要对
docker-desktop发行版这样操作:Docker 的数据请用 Docker 自己的方式清理(见文末维护建议); - 导入后默认登录用户会变回 root,需要在
/etc/wsl.conf里补上:
[user]
default=你的用户名修改后执行 wsl --terminate Ubuntu(或 wsl --shutdown),重新进入 WSL 才会生效。
踩坑:compact 时报“另一个程序正在使用此文件”
执行 compact 时经常遇到:
DiskPart 遇到错误: 另一个程序正在使用此文件,进程无法访问。这说明 vhdx 仍被某个进程占用。按顺序排查:
1. 确认 WSL 真的完全关闭
wsl --shutdown
# 等 10~15 秒,让 vmmem 进程回收
wsl --list --running
Get-Process | Where-Object {$_.Name -like "*vmmem*"}wsl --list --running 应该没有任何输出,vmmem / vmmemWSL 进程也应消失。
注意 wsl --shutdown 只清理当前用户会话的 WSL。如果这台机器有多个 Windows 账户,或者有系统服务在调 WSL,文件仍可能被占用。
2. 关掉 Docker Desktop 和 VS Code
- Docker Desktop:托盘图标右键 → Quit;
- VS Code:关闭所有 Remote-WSL 窗口,否则
wsl.exe会驻留; - Windows Terminal:关掉所有相关标签页。
3. 用资源监视器揪出占用者
这个工具最直接:
Win + R输入resmon回车;- 切到 CPU 标签,展开“关联的句柄”;
- 搜索框输入
ext4.vhdx; - 列表里出现的进程就是元凶,正常退出它。
4. 重启电脑
有些后台软件(杀毒、备份、索引服务)会短暂锁定文件,资源监视器也未必能及时捕捉到。重启后不要打开任何 WSL 相关程序,直接以管理员身份跑 diskpart,是最省心的办法。
5. 记得 detach
如果 compact 是在 attach vdisk readonly 之后失败退出,别忘了先 detach vdisk 再关 diskpart,否则是你自己把文件锁住了。
一些长期维护建议
- 定期执行
sudo fstrim -av再 compact,当作月度保养; - Docker 用户:先用
docker system prune -a清理无用的镜像、容器和构建缓存,清理完再压缩 Docker 的 vhdx。慎用--volumes参数:它会删除所有未被容器使用的数据卷,暂时停掉的数据库容器的数据也可能被一并删除; - 关注 sparse VHD 的修复进展:等微软解决底层数据损坏问题后,这个功能大概率会重新开放,那时候就不用再折腾了;
- 体积大、访问少的文件别放 WSL 里:数据集、虚拟机镜像、归档备份可以放在 Windows 盘符下,通过
/mnt/d/...访问,能显著减缓 vhdx 膨胀。但跨文件系统访问很慢,代码仓库、node_modules、编译产物这类小文件多、读写频繁的目录仍应留在 WSL 内部。
小结
| 方案 | 适用场景 | 风险 |
|---|---|---|
--set-sparse true | 已被官方禁用 | 需 --allow-unsafe,有数据损坏风险,不推荐 |
fstrim + compact vdisk | 日常首选 | 低 |
Optimize-VHD | 有 Hyper-V 环境 | 低 |
| export / import | compact 无效或 vhdx 异常 | 中(需先备份成功) |
一句话结论:WSL2 的 vhdx 只涨不跌是设计使然,df -h 和 Windows 看到的数字对不上是正常现象;最稳妥的做法是 fstrim + diskpart compact,而不是强行开启已经被禁用的稀疏 VHD。