资讯动态

WSL2虚拟磁盘只涨不跌?diskpart压缩VHD回收C盘空间全攻略

发布时间:2026/10/21 13:02:33 来源:尧图企业网站定制
玩 WSL2 的朋友应该都遇到过这个场景某天打开资源管理器发现C盘空间突然告急一个个文件夹排查下来最后定位到 AppData\Local\Packages 下面某个发行版的 ext4.vhdx 动辄十几二十个GB。更气人的是你明明在 Linux 里面删了一堆没用的文件、卸载了几个大型软件包Windows 这边的 VHD 文件体积却纹丝不动。这不是系统出了问题而是 WSL2 的磁盘回收机制本身就是这么设计的——虚拟磁盘只涨不跌想让它瘦下来必须手动压缩 VHD把空间还给 Windows。这篇文章就完整讲清楚这件事为什么 WSL2 的虚拟磁盘会只增不减、压缩前需要做什么准备、diskpart 压缩 VHD 的完整操作链路以及那些我在实操里踩过的坑和事后总结的维护习惯。无论你是刚装 WSL2 的新手还是已经被磁盘占用折磨已久的老用户这篇都值得从头看一遍。1. VHD 为什么会只涨不跌先搞懂 WSL2 的磁盘账本1.1 虚拟磁盘的删了不等于瘦了很多人第一次接触 WSL2 时会有个思维惯性觉得 WSL2 和 Windows 之间就是共享文件夹的关系删了 Linux 里的文件Windows 的可用空间自然就回来了。这个理解在 WSL1 时代大体成立但到了 WSL2架构已经完全不同。WSL2 本质上是一台运行在 Hyper-V 虚拟化层里的轻量虚拟机整个 Linux 文件系统都存放在一个虚拟磁盘文件里也就是我们在 Windows 上看到的 ext4.vhdx。这个文件采用动态扩展机制Linux 里写入数据时VHD 文件跟着字节数增长但反过来Linux 里删除数据时VHD 文件却不会自动收缩。原因在于文件系统层和虚拟磁盘层之间隔着两道账本。Linux 那边运行的是 ext4 文件系统删除文件后ext4 只会把对应的数据块标记为“空闲”并不会主动告诉虚拟磁盘驱动“这些扇区可以清零了”。VHDX 格式虽然在设计上支持 trim 操作但 WSL2 默认情况下的虚拟磁盘并不会时刻把这些释放出来的空间映射回 Windows 文件系统。一层层剥离后最终的结果就是你在 Linux 里删了 10GB 文件Windows 上的 ext4.vhdx 文件大小还是老样子那 10GB 空间继续被这个文件占着成了“内部空闲、外部占用”的悬空空间。可以类比成一个带锁的仓库仓库管理员ext4知道哪些货架空了但仓库大门VHD 文件始终为单位租着出租方Windows不会主动来盘点里面到底还有多少东西。1.2 谁在偷偷吃掉你的 C 盘典型的 VHD 膨胀来源明白了原理再来看哪些行为最容易让 VHD 快速膨胀就很有针对性了。根据我自己的观察和帮朋友排查的经历绝大多数 VHD 体积失控都逃不出下面这几类来源Docker 镜像和容器这是最大头。Docker 的 overlay 文件系统会不断累积镜像层docker build产生的中间层、运行时挂载的卷全部落在 WSL2 的虚拟磁盘里。很多人删了容器、跑了docker system prune但镜像层里的历史数据并不会完全清干净。包管理器和编译缓存apt下载的 deb 包、pip的 wheel 缓存、npm的缓存还有 C/C 编译产生的中间文件日常觉得每个都很小架不住日积月累。日志文件journald 的系统日志、各种服务的 log尤其是跑了好几个容器的情况下日志写得特别勤快。数据库和搜索索引直接在 WSL2 里跑 MySQL、PostgreSQL、Elasticsearch数据文件会长期占据大量空间。个人资料文件比如把大文件下载、解压到了 Linux 家目录用完即弃但虚拟磁盘已经为它们“胖”了起来。所以当你发现自己的 ext4.vhdx 明显偏大时别急着压缩先想清楚里面到底装了什么。压缩只是物理回收手段如果空间是被真实占用着压完过阵子还是会涨回去这属于治标不治本。1.3 自查方法你的 VHD 到底胖不胖、值不值得压动手之前先量化一下当前的状况。在 Windows 终端里跑wsl -l -v能看到所有发行版的名称和运行状态。然后打开资源管理器进入%LOCALAPPDATA%\Packages找到对应发行版的目录比如 Ubuntu 的包名通常是CanonicalGroupLimited.Ubuntu...进入LocalState文件夹就能看到ext4.vhdx右键属性查看“大小”和“占用空间”。Linux 侧同样跑一个命令对比真实数据df -h /看/挂载点下的Used和Avail两个值。这时候会出现典型的“对不上账”Windows 里ext4.vhdx实际大小是 30GBLinux 里df -h显示已用只有 15GB。这中间的差额就是悬空空间也是你压缩 VHD 能收回来的部分。如果两个数字非常接近说明虚拟磁盘里的空间基本都是实打实被占用的压缩效果会非常有限。这种情况就别指望压缩能变出多少空间先把该清理的东西清掉再说。反之如果差额有 5GB 以上那这篇文章后面写的每一步都值得照做。2. 压缩前的三件套备份、定位、彻底关机2.1 不要嫌麻烦wsl --export 备份的正确姿势VHD 压缩本质上是对虚拟磁盘文件做一次物理级的整理和收缩虽然正常操作下不会丢数据但谁也不能保证中途不出现断电、文件系统校验出问题、或者手滑执行错命令之类的意外。所以无论你多赶时间我都会建议先做一次完整导出。wsl --shutdown wsl --export Ubuntu D:\wsl-backup\ubuntu-backup.tar注意第一个wsl --shutdown是必须的导出和后续压缩操作都要求在 WSL 完全停止的状态下进行。第二个命令会把整个发行版的文件系统打包成 tar 文件放到你指定的位置建议放在非系统盘避免备份本身又占用 C 盘空间。如果你平时把发行版装在了自定义盘符比如 E 盘导出时同样能处理只是导出的目标路径写清楚就行。导出需要多长时间、生成多大的 tar取决于你虚拟磁盘里真实占用的数据量不是 VHD 文件的大小。我自己的例子是真实数据只有 8GB但 VHD 膨胀到了 25GB导出大概花了两三分钟。如果你是几十GB的大型数据记得给备份留足时间。2.2 找到 ext4.vhdx 的真实位置与真实体积刚才提到默认安装路径在%LOCALAPPDATA%\Packages下但如果你当初用了wsl --import手动安装到指定目录或者 Windows 商店应用有移动过路径就会不一样。更稳妥的定位方法是在 PowerShell 里查询(Get-ChildItem -Recurse -Filter ext4.vhdx $env:LOCALAPPDATA\Packages -ErrorAction SilentlyContinue | Select-Object FullName, {NameSizeGB; Expression{[math]::Round($_.Length/1GB,2)}})这会递归搜索所有发行版的 vhdx 文件并显示大小一目了然。如果你的 WSL 发行版是自定义安装的就直接去你安装时指定的目录找。在这个阶段顺便记录一下 VHD 当前的原始大小压缩完成后对比用你就能知道自己到底找回了多少空间。2.3 让 WSL2 完全停下来的正确姿势压缩 VHD 之前WSL2 的所有发行版都必须处于完全停止状态。很多人以为关掉终端窗口就等于关机了其实 WSL2 的后台进程还会继续驻留一小段时间或者被某些配置带起来常驻。最彻底的手段还是wsl --shutdown执行完之后可以再确认一下状态wsl -l --running如果输出为空说明没有运行中的发行版了。这时候去任务管理器里看一眼只要没有名为“Vmmem”或“WSL”的进程占着内存就说明虚拟机基础设施已经停妥。如果还有残留进程可以稍微等十几秒或者用管理员身份在 PowerShell 里执行一次重启服务不推荐但确实有效net.exe stop LxssManager 2$null; net.exe start LxssManager 2$null当然这招会中断所有 WSL 相关进程不到万不得已不用。3. diskpart 压缩实操从附加到分离的完整链路3.1 attach readonly 到底为什么必须只读核心操作环节来了。Windows 自带的diskpart工具就内置了compact vdisk命令不需要安装任何第三方软件。你可以把它理解为 Windows 侧的磁盘管理员入口但它以命令行方式操作非常适合做这类脚本化、精准化的虚拟磁盘管理。用管理员权限打开命令提示符或 PowerShell依次输入diskpart进入 diskpart 交互模式后先选中目标 VHD 文件select vdisk fileC:\Users\你的用户名\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu...\LocalState\ext4.vhdx这里有一个很关键的细节给路径加双引号尤其是路径里含空格或中文时少了引号 diskpart 往往解析不了。接着附加虚拟磁盘这步让系统把 VHD 挂载成一个虚拟磁盘但要注意必须带readonly参数attach vdisk readonly为什么不直接attach vdiskcompress 操作要求虚拟磁盘在附加状态下只读目的是防止在压缩过程中有写入操作改变文件系统状态。WSL 已经停机VHD 是静止的只读附加是压缩的安全前提同时也避免后续误操作对文件系统产生不可逆影响。3.2 compact vdisk 的原理与执行过程附加完成以后执行压缩compact vdisk这条命令的原理值得展开说两句。它做的事情分两步首先对虚拟磁盘里的空闲扇区做 trim 和清零处理把空洞标记为可回收然后物理地收缩 VHDX 文件把尾部那些“全是洞”的块真正从文件里移除让文件大小变小。整个过程可以理解成整理房间先把你不要的东西挑出来堆在一起再把这堆垃圾统一搬走房间的占地面积自然就缩小了。执行过程中命令窗口只会显示“正在压缩…”没有任何进度条这是很多人心里没底的地方。短则一两分钟长则十几分钟具体看要处理的数据量和磁盘速度。我在一块普通 SATA SSD 上压缩过 25GB 的 VHD实际耗时大约 5 分钟。整个过程里不要关闭窗口、不要重启电脑、不要额外去打开那个 vhdx 文件给工具足够的时间把活儿干完。压缩完成后执行分离detach vdisk exit退出 diskpart。到这一步为止一次完整的压缩操作就结束了。3.3 验证结果怎么确认空间是真的还给 Windows 了完成后第一时间做两件事。先到刚才的LocalState目录刷新一下文件夹看ext4.vhdx的“大小”字段。理想情况下数字应该已经明显缩小。以我上面那个例子25GB 的 VHD 压缩完变成了 9GB 左右回收了 16GB 空间到 Windows 侧。然后重新启动 WSL直接在 Windows 终端里输入wsl进入发行版跑一次df -h /正常情况下Linux 里的数据完好无损已用空间保持原样可用空间因为文件系统本身的元数据没变化也基本不变。VHD 文件缩小了但 Linux 内部看到的一切还和压缩前一模一样——这就是虚拟磁盘回收空间后文件系统完全无感的效果。如果发现 WSL 启动异常或者进入后报文件系统错误先别慌。你还有备份用wsl --unregister移除坏掉的发行版后再用wsl --import把 2.1 节导出的 tar 恢复回来。不过这种情况极其少见正常操作基本不会触发。4. 别急着高兴压缩失败和高占用通常栽在这几处4.1 diskpart 各种报错的排错思路实操中不少人会碰到报错我把常见的几类整理成一张表方便对照排查。报错现象大概率原因解决方式“找不到指定的虚拟磁盘文件”路径拼写错误或引号没加复制完整路径确保加双引号“指定的虚拟磁盘不支持此操作”选中的不是 VHDX 文件或 WSL 尚未完全关闭确认文件扩展名再跑一次wsl --shutdown“无法附加”权限不足或文件被占用以管理员身份运行 diskpart检查资源管理器是否打开了该目录压缩过程卡死很久不动磁盘空间不足、VHD 文件过大或磁盘老化尝试增加更多空闲空间再压缩给足耐心等待最容易被忽略的是磁盘可用空间不足。compact 操作虽然看起来是原地整理但它在 trim 阶段会产生临时写入如果 Windows 系统盘本身剩余空间不到 VHD 大小的 10%极有可能中途失败报错。所以压缩前先看一眼 C 盘剩余空间低于别急着压缩先腾挪一些空间出来再动手。4.2 为什么说只压缩VHD是治标不治本如果你压缩完过了一两个月发现 VHD 又膨胀回来了这是很正常的事。真正的解法应该是两条腿走路一方面做源头清理把 VHD 里确实不需要的大文件干掉另一方面定期做物理回收。压缩是个低风险但比较“笨”的操作它不关心你里面装了什么只负责把空闲扇区腾出来还回 Windows。我自己见过最极端的例子某个开发者朋友从来不在 Linux 里清 docker 镜像半年下来 VHD 从 20GB 涨到 90GB里面真实数据只有 10GB。压缩一次固然能瘦下来但如果不改变使用习惯几个月后照样打回原形。所以在压缩前、压缩中、压缩后都不妨追问一句这些空间是被什么吃掉的能不能从源头省下来4.3 Windows 11 的 sparse vhd 选项能省掉手动压缩吗说到源头治理这里有一个 Windows 11 上很好用的新特性值得提一嘴。新版本的 WSL 支持通过命令把 VHD 切换到稀疏模式wsl --manage Ubuntu --set-sparse true开启之后虚拟磁盘会尝试在文件系统释放空间时同步收缩减少了手动压缩的频次。但这不是银弹稀疏模式在某些硬件环境下可能带来轻微性能损耗尤其是频繁读写小文件的场景。如果你的 WSL2 主要跑数据库、编译任务对磁盘 IO 比较敏感我建议保持默认模式手动定时压缩反而更稳妥。另外注意这个命令只能用于wsl --manage并且仅在支持新版 WSL 的 Windows 11 系统上可用。Windows 10 用户还是老老实实走 diskpart 路线。5. 源头治理让 VHD 长期保持苗条的实操清单5.1 先做大扫除再压缩性价比最高的删除项既然只压缩不清理等于白忙活那我就把我每次压缩前都会过一遍的清理清单列出来。这些操作全部在 Linux 侧执行安全、见效快# 1. APT 缓存与无用包 sudo apt autoclean sudo apt autoremove -y # 2. Docker 残留镜像、无主卷、构建缓存 docker system prune -a --volumes docker builder prune -a # 3. journald 日志按时间截断 sudo journalctl --vacuum-time7d # 4. pip 与 npm 缓存 pip cache purge npm cache verify # 5. 用户级缓存目录重点看看有没有旧版本工具留下的历史数据 du -sh ~/.cache 2/dev/null tail -n 1 ~/.bashrc /dev/null以上五类清理做完一般能少个 2GB 到 10GB 不等取决于你平日的使用习惯。清理完成后跑一遍df -h /确认空间确实释放出来了然后再去 Windows 侧执行 diskpart 压缩。这样压缩效率更高回收空间更明显也能尽量避免那种“压缩完没多少变化”的落差感。5.2 定时巡检与自动化的偷懒方案最后聊聊维护节奏。压缩 VHD 不需要天天做那太折腾了但也不需要等到 C 盘满了再临时抱佛脚。我现在的习惯是每两个月做一次例行巡检看一眼ext4.vhdx的实际大小。进 Linux 跑df -h /对比差额。如果差额超过 5GB就执行一遍上面的清理加压缩流程。如果想更省事一点你们完全可以把 diskpart 那几步写成一个脚本文件双击执行就行。核心逻辑就是select、attach、compact、detach脚本里把路径和引号处理好即可。不过要注意脚本必须以管理员身份运行否则 diskpart 会因为权限不足直接拒绝执行。另外一个偷懒技巧尽量别把所有发行版都装默认 C 盘。如果你经常使用 WSL2可以考虑重新导入到 D 盘或其他空间充裕的盘符从源头避免 C 盘被 WSL2 拖垮。自己装的新发行版直接用wsl --import指定路径之前已经装在 C 盘的也可以导出后用wsl --import迁移到新位置不过这是另一个话题了。结合我自己的经验压缩 VHD 操作本身不难真正难的是养成定期维护的习惯。第一次做的时候花个半小时把流程跑通之后每两个月花十几分钟例行清理压缩C 盘基本上再也不会被 WSL2 的虚拟磁盘悄悄吃空。至少在我自己的机器上这套流程跑了两年C 盘空间一直稳稳当当再也没出现过因为 ext4.vhdx 膨胀而手忙脚乱的情况。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑