跳到主内容
芝麻园地
← 返回博客
#wsl2#vhdx#disk-space#virtualization#troubleshooting

WSL2 删除文件后磁盘空间没变多?VHDX 虚拟磁盘瘦身实战

在 WSL2 里删掉了 50G 的模型文件,Windows 侧磁盘空间却纹丝不动——这是 WSL2 动态扩展虚拟磁盘(VHDX)的典型行为。本文记录完整排查过程与三种瘦身方案:fstrim、稀疏 VHD 模式(WSL 2.7+)、Optimize-VHD/diskpart 压缩,并详解"稀疏文件无法压缩"这个最容易踩的坑。

编程快车 11 分钟

场景:在 WSL2(Ubuntu-24.04)里 rm -rf 删掉了约 50GB 的大文件(一个弃用的 LLM 模型),回到 Windows 一看 C 盘可用空间几乎没有变化。本文记录完整的排查过程和最终解法,涉及 fstrim、WSL 稀疏 VHD 模式、Optimize-VHD / diskpart 压缩,以及一个很容易踩的坑。

1. 现象

  • WSL 内确认文件已删除:/home/dev/models/ 下只剩需要的模型目录,du -sh 确认大目录已消失
  • 但 Windows 侧 C 盘可用空间没有回升(实测仅剩 ~22GB,几乎没有变化)
  • 执行 wsl --shutdown 重启后依然如此

2. 根因:WSL2 的磁盘是一块“只涨不缩”的虚拟硬盘

WSL2 不是直接使用 Windows 文件系统,而是把发行版装在一个动态扩展的虚拟硬盘(VHDX)文件里。它的工作方式:

  • VHDX 文件会随使用自动增长(写入越多文件越大)
  • 但删除文件只释放 VHDX 内部的逻辑空间,文件本身不会自动缩小——那些块只是被标记为“空闲”,Windows 侧的 C 盘空间依然被占用
  • 只有显式执行“压缩/收缩”操作,VHDX 才会把尾部空闲块归还给 Windows

所以“WSL 里删了文件 → Windows 空间变多”是不成立的,必须主动收缩 VHDX。

3. 排查:找到 VHDX 并确认空洞

3.1 找到发行版对应的 VHDX 路径

发行版的虚拟磁盘位置存在注册表里:

Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" |
  ForEach-Object {
    $p = Get-ItemProperty $_.PSPath
    [PSCustomObject]@{ Distro=$p.DistributionName; BasePath=$p.BasePath }
  }

我机器的结果(Windows 11 / WSL 2.7.13):

Distro      : Ubuntu-24.04
BasePath    : C:\Users\Dev\AppData\Local\wsl\{7681ba24-e842-452b-9fc7-1502c48565ff}

VHDX 文件就是 BasePath\ext4.vhdx。

3.2 对比“内部使用”与“文件大小”,确认空洞

WSL 内部视角(df -h /):

Filesystem  Size   Used  Avail  Use%  Mounted on
/dev/sdd    1007G  165G  792G   18%  /

Windows 视角(VHDX 文件大小):

VHDX: C:\Users\Dev\AppData\Local\wsl\{...}\ext4.vhdx
Size: 221.6 GB

内部只用 165G,文件却占 221.6G → 约 56GB 是“已删除但未回收”的空洞,这就是丢失空间的来源。

4. 解决方案(按推荐顺序)

方案 A:WSL 2.7+ 稀疏 VHD 模式(一劳永逸)

WSL 2.7 起支持把 VHD 转换为稀疏模式(sparse VHD):开启后,在 WSL 里删除文件会自动把空间归还给 Windows,无需再手动压缩。

# 1. 先让文件系统标记可回收块(需要 root)
wsl -d Ubuntu-24.04 -u root -- fstrim -v /
# 输出示例:/: 841 GiB (902995951616 bytes) trimmed

# 2. 关闭 WSL
wsl --shutdown

# 3. 转换为稀疏模式(必须带 --allow-unsafe)
wsl --manage Ubuntu-24.04 --set-sparse true --allow-unsafe

# 4. 验证(应显示 "This file is set as sparse")
fsutil sparse queryflag "C:\...\ext4.vhdx"

⚠️ 注意:稀疏模式对开启前已存在的空洞不自动回收(实测 fstrim + 挂载后 C 盘空间无变化)。也就是说:转换前删除的大文件,需要额外做一次压缩(见方案 B)才能释放;转换后新删除的文件才会自动回收。

方案 B:Optimize-VHD 压缩(处理存量空洞,需管理员)

先确认 WSL 已 wsl --shutdown,然后以管理员身份打开 PowerShell 执行:

Optimize-VHD -Path "C:\Users\Dev\AppData\Local\wsl\{7681ba24-e842-452b-9fc7-1502c48565ff}\ext4.vhdx" -Mode Full

预期效果:VHDX 从 221.6 GB 收缩到约 165 GB(接近内部实际使用量),一次性释放约 55 GB。

如果系统没有 Hyper-V 模块,可用 diskpart 代替(同样管理员):

diskpart
select vdisk file="C:\Users\Dev\AppData\Local\wsl\{7681ba24-e842-452b-9fc7-1502c48565ff}\ext4.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit

完整流程(我的实际顺序,一次成功)

1. wsl -d Ubuntu-24.04 -u root -- fstrim -v /      # 标记可回收块
2. wsl --shutdown                                   # 关闭 WSL(释放文件句柄)
3. wsl --manage Ubuntu-24.04 --set-sparse true --allow-unsafe   # 开启稀疏模式
4. Optimize-VHD ... -Mode Full                      # 管理员,压缩存量空洞

5. 最容易踩的坑:稀疏文件无法压缩

在我先开启了稀疏模式(方案 A)之后,管理员运行 Optimize-VHD 直接报错:

Optimize-VHD: 无法压缩虚拟磁盘。
无法打开附件"...ext4.vhdx"。错误:"由于虚拟磁盘系统限制,无法完成请求的操作。
虚拟硬盘文件必须是未压缩和未加密的文件,并且不能是稀疏文件。"

原因:稀疏 VHD 和 Optimize-VHD/diskpart 压缩是互斥的——压缩工具要求文件是非稀疏的普通动态 VHD。

解法:先转回动态模式,再压缩:

wsl --manage Ubuntu-24.04 --set-sparse false --allow-unsafe
fsutil sparse queryflag "C:\...\ext4.vhdx"   # 应显示 "This file is NOT set as sparse"
# 然后管理员运行 Optimize-VHD

如果你的诉求是”一次释放存量空间“,直接走方案 B 即可,不用开稀疏模式;如果诉求是”以后删除自动释放“,开稀疏模式,但存量空洞仍需一次方案 B 压缩收尾。

6. 日常维护建议

  1. 优先考虑稀疏模式(WSL 2.7+):一次配置,以后删除文件自动回收空间,不用再手动压缩
  2. 定期压缩(非稀疏模式):删除大文件后执行 fstrim → wsl --shutdown → Optimize-VHD,可以写成一个脚本
  3. 监控:关注 df -h / 与 VHDX 文件大小的差距,差距越大说明空洞越多
  4. 注意 C 盘空间:如果 C 盘可用空间本身很小(例如 <30GB),压缩前先确认有足够临时空间,并留意 set-sparse false 转换时的空间需求
  5. 区分两种“空间”:WSL 内部 df 显示的是虚拟磁盘内部分区使用;Windows 资源管理器看的是 VHDX 文件占用——两者不相等是正常的,别被吓到

7. 结论

WSL2 删除文件不释放 Windows 磁盘空间,是动态 VHDX 的正常机制而非故障。解决路径清晰:fstrim 标记 → wsl --shutdown → 开启稀疏模式或 Optimize-VHD 压缩。注意稀疏模式与压缩工具互斥,先转换再压缩即可。实测可一次性回收约 55 GB 空间。