WSL2 虚拟磁盘 ext4.vhdx 占用过大缩减教程
本文适用于这种情况:Windows 里看到某个 WSL2 的 ext4.vhdx 文件很大,比如 90GB、100GB,但进入 WSL 后用 df -h 查看,Linux 内部实际只用了十几 GB。出现这种情况并不是 WSL 坏了,而是因为 WSL2 使用的是动态虚拟硬盘。它会随着使用自动变大,但你在 Linux 里删除文件后,外层的 ext4.vhdx 通常不会自动缩小,所以需要手动压缩。
以 Ubuntu-22.04 为例,假设它的虚拟磁盘路径类似下面这样:
C:\Users\xxx\AppData\Local\wsl\{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\ext4.vhdx
这个路径是正常的。不同 WSL 发行版可能在不同目录下,例如有的在:
C:\Users\xxx\AppData\Local\Packages\...\LocalState\ext4.vhdx
也有的在:
C:\Users\xxx\AppData\Local\wsl\{GUID}\ext4.vhdx
路径不同只是安装方式不同,不代表异常。真正要判断的是:Windows 看到的 ext4.vhdx 大小,和 WSL 内部实际使用量是否差距很大。
查询 WSL 名称和状态
先打开 PowerShell,执行:
wsl -l -v
你会看到类似:
NAME STATE VERSION
* Ubuntu-22.04 Running 2
Ubuntu-20.04 Stopped 2
Ubuntu-24.04 Stopped 2
这里要确认你准备缩减的是哪个发行版。后面的教程以:
Ubuntu-22.04
为例。如果你的名字不同,比如 Ubuntu、Ubuntu-24.04,后面命令里的名字要对应修改。
查询 WSL 的真实磁盘路径
不要自己去文件夹里乱找,也不要手动复制很长的 GUID 路径。最稳的方法是从注册表里读取 WSL 的真实路径。
在 PowerShell 中执行:
Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" |
ForEach-Object {
$p = Get-ItemProperty $_.PSPath
[PSCustomObject]@{
Name = $p.DistributionName
BasePath = $p.BasePath
Version = $p.Version
}
}
你会看到类似:
Name BasePath
---- --------
Ubuntu-22.04 C:\Users\xxx\AppData\Local\wsl\{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
Ubuntu-20.04 C:\Users\xxx\AppData\Local\Packages\...\LocalState
Ubuntu-24.04 C:\Users\xxx\AppData\Local\Packages\...\LocalState
其中 BasePath 就是对应 WSL 虚拟磁盘所在的目录。
自动获取 Ubuntu-22.04 的 ext4.vhdx 路径
为了避免路径输错,建议直接用变量保存路径。执行:
$p = Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" |
ForEach-Object { Get-ItemProperty $_.PSPath } |
Where-Object { $_.DistributionName -eq "Ubuntu-22.04" }
$vhd = Join-Path $p.BasePath "ext4.vhdx"
$vhd
Test-Path -LiteralPath $vhd
正常情况下会输出类似:
C:\Users\xxx\AppData\Local\wsl\{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\ext4.vhdx
True
只要最后是:
True
就说明路径正确。
如果是:
False
说明没有找到 ext4.vhdx,这时不要继续压缩,先检查发行版名字是不是写错了。
查看压缩前 ext4.vhdx 的大小
继续在 PowerShell 中执行:
Get-Item -LiteralPath $vhd |
Select-Object FullName,@{Name="SizeGB";Expression={[math]::Round($_.Length/1GB,2)}}
你会看到类似:
FullName SizeGB
-------- ------
C:\Users\xxx\AppData\Local\wsl\{...}\ext4.vhdx 95.65
这里的 SizeGB 就是 Windows 当前实际被这个虚拟磁盘文件占用的空间。
查看 WSL 内部实际使用量
接着查看 Ubuntu-22.04 内部到底用了多少空间:
wsl -d Ubuntu-22.04 -- df -h /
如果结果类似:
Filesystem Size Used Avail Use% Mounted on
/dev/sdc 1007G 17G 939G 2% /
说明 Linux 内部实际只用了 17GB。此时如果 Windows 上的 ext4.vhdx 是 95GB,就说明中间有大量已经释放但没有被 Windows 回收的空间,可以进行压缩。
如果这里 Used 也很大,比如 80GB、90GB,那就不是虚拟磁盘没缩的问题,而是 WSL 里面确实有大文件,需要先清理 Linux 内部文件,再压缩。
执行 fstrim 标记可回收空间
在压缩之前,先让 WSL 标记哪些空间已经不用了:
wsl -d Ubuntu-22.04 -- bash -lc "sudo fstrim -av"
这个命令可能会要求输入 Ubuntu 里的 sudo 密码。执行后可能会看到类似:
/: 70 GiB trimmed
这表示 Linux 内部有一部分空闲块已经被标记为可以回收。即使输出比较少,也可以继续下一步。
关闭 WSL
压缩虚拟磁盘前,必须关闭 WSL。执行:
wsl --shutdown
然后检查状态:
wsl -l -v
最好确认 Ubuntu-22.04 已经是:
Stopped
如果还显示 Running,等几秒后再执行一次:
wsl --shutdown
使用 DiskPart 压缩 ext4.vhdx
如果你的系统支持 Optimize-VHD,可以使用它。但很多 Windows 家用版或未启用 Hyper-V 模块的系统没有这个命令,所以这里直接使用更通用的 DiskPart 方法。
建议用管理员 PowerShell执行下面这一整段:
$p = Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" |
ForEach-Object { Get-ItemProperty $_.PSPath } |
Where-Object { $_.DistributionName -eq "Ubuntu-22.04" }
$vhd = Join-Path $p.BasePath "ext4.vhdx"
if (-not (Test-Path -LiteralPath $vhd)) {
Write-Host "找不到 VHDX 文件:$vhd"
exit
}
wsl --shutdown
@"
select vdisk file="$vhd"
attach vdisk readonly
compact vdisk
detach vdisk
exit
"@ | Set-Content "$env:TEMP\compact-wsl.txt" -Encoding ASCII
diskpart /s "$env:TEMP\compact-wsl.txt"
这段命令会自动找到 Ubuntu-22.04 的 ext4.vhdx,生成 DiskPart 脚本,然后执行压缩。这样做的好处是不用你手动输入长路径,也不会因为 GUID 少输一个字符而失败。
成功时,DiskPart 一般会显示类似:
DiskPart 成功地选择了虚拟磁盘文件。
DiskPart 成功地附加了虚拟磁盘文件。
DiskPart 成功地压缩了虚拟磁盘文件。
DiskPart 成功地分离了虚拟磁盘文件。
其中最关键的是:
DiskPart 成功地压缩了虚拟磁盘文件。
验证压缩结果
压缩完成后,再查看一次 ext4.vhdx 的大小:
Get-Item -LiteralPath $vhd |
Select-Object FullName,@{Name="SizeGB";Expression={[math]::Round($_.Length/1GB,2)}}
如果 PowerShell 变量 $vhd 已经失效,就重新执行:
$p = Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" |
ForEach-Object { Get-ItemProperty $_.PSPath } |
Where-Object { $_.DistributionName -eq "Ubuntu-22.04" }
$vhd = Join-Path $p.BasePath "ext4.vhdx"
Get-Item -LiteralPath $vhd |
Select-Object FullName,@{Name="SizeGB";Expression={[math]::Round($_.Length/1GB,2)}}
正常情况下,原来 95GB 左右的 ext4.vhdx 可能会降到 20GB 到 30GB 左右。不一定会刚好等于 df -h 里的 17GB,因为虚拟磁盘还会保留文件系统元数据和一些结构空间。
只要它明显变小,就说明缩减成功。
启动 WSL 检查是否正常
最后重新启动 Ubuntu-22.04:
wsl -d Ubuntu-22.04
进入后执行:
df -h /
确认系统可以正常进入,空间显示正常即可。
退出 WSL:
exit
最终推荐命令合集
如果你已经确认要缩减的是 Ubuntu-22.04,可以直接用下面这段。用管理员 PowerShell运行:
$p = Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" |
ForEach-Object { Get-ItemProperty $_.PSPath } |
Where-Object { $_.DistributionName -eq "Ubuntu-22.04" }
$vhd = Join-Path $p.BasePath "ext4.vhdx"
Write-Host "目标 VHDX:$vhd"
if (-not (Test-Path -LiteralPath $vhd)) {
Write-Host "错误:找不到 VHDX 文件,停止操作。"
exit
}
Write-Host "`n压缩前大小:"
Get-Item -LiteralPath $vhd |
Select-Object FullName,@{Name="SizeGB";Expression={[math]::Round($_.Length/1GB,2)}}
Write-Host "`n执行 fstrim..."
wsl -d Ubuntu-22.04 -- bash -lc "sudo fstrim -av"
Write-Host "`n关闭 WSL..."
wsl --shutdown
@"
select vdisk file="$vhd"
attach vdisk readonly
compact vdisk
detach vdisk
exit
"@ | Set-Content "$env:TEMP\compact-wsl.txt" -Encoding ASCII
Write-Host "`n开始压缩 VHDX..."
diskpart /s "$env:TEMP\compact-wsl.txt"
Write-Host "`n压缩后大小:"
Get-Item -LiteralPath $vhd |
Select-Object FullName,@{Name="SizeGB";Expression={[math]::Round($_.Length/1GB,2)}}
这版流程就是:先自动找到 Ubuntu-22.04 的 ext4.vhdx,显示压缩前大小,执行 fstrim,关闭 WSL,使用 DiskPart 压缩,最后显示压缩后大小。路径里的用户名和 GUID 都不需要手动输入,避免出错。