一个对不上的账
前几天做例行磁盘体检,发现一个对不上的账:
- Windows 这边看 C 盘:949G 的盘,已用 652G,红了。
- 但在 WSL(Ubuntu)里跑
df -h /:只用了 135G。
中间差着 500 多 G。按常识,我在 Linux 里删一个文件,Windows 上的占用就该跟着降——我前一晚刚删了 62G 的视频产物,理论上 C 盘应该松一大截。可它纹丝不动。
这账对不上,就值得查。
排查:钱到底进了哪个口袋
我没有上来就删东西,而是先一层层把空间分布摸清楚。C 盘太大(600多G),du 全盘扫太慢,改用 PowerShell 直接问 Windows,按目录大小排序:
| |
结果一目了然:
| |
往下钻 C:\Users\<我>\AppData\Local:
| |
再看到具体文件:
| |
两个文件加起来 373G。元凶找到了。
根因:vhdx 是"只增不减"的
这两个 .vhdx 是 WSL2 和 Docker Desktop 的虚拟硬盘文件。你整个 Linux 系统、所有 Docker 镜像,最终都存在这两个文件里。
关键在于它的增长策略:只增不减。
打个比方——它像一个不断往里塞东西的衣柜:
- 你往 Linux 里写文件,Windows 就按需把 vhdx 文件撑大一点,腾出"格子"。
- 你在 Linux 里
rm删掉文件,只是在衣柜里把那个格子标记为"空",衣柜本身(vhdx 文件)一点都不缩。
所以会出现这种魔幻现象:我在 Linux 里越删越狠,C 盘越涨越凶。vhdx 内部其实已经有 130G 是"空洞"了,但 Windows 看到的还是那个 264G 的大家伙。
Docker 那个也一样——我刚 docker system prune 清了 20多G 镜像和构建缓存,docker_data.vhdx 还是 108G。
解决:手动 compact 把空洞挤掉
既然不会自动缩,就手动压缩。思路是把 vhdx 以只读方式挂载,再用 diskpart 的 compact vdisk 把内部的空洞物理挤掉。
全程在 Windows 端操作,因为得先
wsl --shutdown关掉 WSL——总不能一边踩着刹车一边让车自己跑。
第一步:关掉 WSL 和 Docker
在 Windows 里右键开始菜单 → 终端(管理员) / PowerShell(管理员):
| |
第二步:diskpart 压缩 vhdx
对每个 vhdx 文件跑一遍(把路径换成你自己的):
| |
进入 DISKPART> 提示符后:
| |
compact vdisk 会跑几分钟(取决于文件多大、有多少空洞),进度条走完就收工。
Docker 那个同理:
| |
找不到自己的 vhdx 在哪?微软官方给了一条 PowerShell 命令,替换成你的发行版名(比如
Ubuntu)即可:
1 2(Get-ChildItem HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss | Where-Object { $_.GetValue("DistributionName") -eq 'Ubuntu' }).GetValue("BasePath") + "\ext4.vhdx"
实际效果
我这台机器:
| 文件 | 压缩前 | 压缩后 | 释放 |
|---|---|---|---|
WSL ext4.vhdx | 264.7G | ~140G | ~124G |
Docker docker_data.vhdx | 108.3G | ~80G | ~28G |
C 盘可用空间从 298G 直接回到 440G 左右,一下多出 150G。整个过程不动任何 Linux 文件,只是把"空洞"物理挤掉,零风险。
顺手清掉的几类"真空垃圾"
排查途中还发现了几处真能删的,一并记录:
- Docker 三件套:
docker container prune、docker image prune -a、docker builder prune—— 我这台清出 20多G(悬空镜像 + 构建缓存)。注意image prune -a会删掉没在跑的镜像,先确认没有要留的。 - VS Code 插件更新残留:
.vscode\extensions下堆了 27 个.xxxxxxxx-xxxx这种 GUID 命名的隐藏目录,是 Codex / Claude Code 插件每次更新留下的旧版本尸体,每个塞一个 100-335M 的codex.exe/claude.exe。清掉 6.9G,正在用的插件不受影响。 - 各类包管理器缓存:
~/.npm、~/.bun/install/cache、~/.cache、pip/pnpm store —— 删了会自动重建,放心清。
几点提醒
- vhdx 压缩是安全的,但操作前确认 WSL 里没有正在编辑未保存的内容(
wsl --shutdown会断开所有终端)。 - 如果压缩效果不理想,先在 WSL 里跑一次
sudo fstrim -av(把文件系统的空闲块标记出来),再回 Windows 执行 compact,回收会更彻底。 - 想一劳永逸:WSL 较新版本支持在
.wslconfig里给 vhdx 设上限,以及开启稀疏 vhdx(sparseVhd=true)让文件按需增长、回落更积极。具体可参考微软官方的 WSL 磁盘管理文档。 - 别用 Windows 的资源管理器/编辑器直接去改 AppData 里的 WSL 文件,容易把发行版搞坏。要从 Windows 访问 Linux 文件,走
\\wsl$\<发行版名>这个共享路径。
结语
这次的账其实很简单:C 盘红不是因为我东西多,而是因为两个虚拟磁盘只吃不拉。删文件在 Linux 里是"逻辑删除",落到 Windows 的 vhdx 上就成了"只标记不回收"。
碰到"C盘满了但不知道被谁占了"的情况,别急着格式化重装——先把空间一层层排出来,八成是某个只增不减的容器文件在悄悄膨胀。找到它,compact 一下,空间就回来了。