返回文章
NO.
029
DATE
READ
~7 min
KIND
案例
STATUS
原文

Windows 内存释放大法:折腾半天,最后发现真正吃内存的是我自己

16GB Windows 11 + WSL2 + VMware 环境下,从 WSL、非分页池、WebView2 一路排到 Widgets、Search 和传递优化;最后 Apply/Restore 实测几乎没差,真正的大头还是 Chrome 和自己开的应用。

最近电脑经常动不动内存就往 90% 上边跑。虽然只是 16GB 吧,但是我也没开什么特别大的应用,而且更离谱的是:有时候刚开机,只开几个小的自启动应用,内存就已经七八十了。

这还怎么玩,是时候优化一下了。

先把结论放前面:这次折腾最后变成了一次失败的“内存优化”,但算是一次挺成功的排查。

我从 WSL、非分页池一路查到 WebView2,后边甚至还写了一个脚本去关 Windows Widgets、搜索里的联网内容和传递优化 P2P。脚本的 Apply / Restore 都跑通了,结果——内存基本没区别。

真正的大头其实从头到尾就写在监控输出里:Chrome、WebView2 宿主应用、ChatGPT,以及我自己开的那堆东西。

哈哈哈哈,白搞。

我的环境

机器是惠普暗影精灵 8:16GB 内存、i7-12700H、RTX 3050,原装 512GB SSD,后来又加了一块 2TB 固态。

日常环境也不算特别“轻”:

  • Windows 11 宿主机;
  • WSL2 Ubuntu 24.04,里面跑一些服务和自启动任务;
  • VMware 里还有一台 Ubuntu 26.04;
  • Chrome(无底洞);
  • ChatGPT / Codex Desktop;
  • 代理、坚果云、Telegram、Typora、PowerToys 等常驻应用。

所以一开始我最怀疑的其实是 WSL。

第一刀先砍 WSL:限制到 4GB

先让 Codex CLI 在 WSL 里排了一轮,没定位出什么异常,最后先做了一个比较保守的动作:给 WSL2 设个内存上限。

%userprofile%\.wslconfig

[wsl2]
memory=4GB
processors=4

改完之后要执行:

wsl --shutdown

然后重新启动发行版才会生效。

这里注意一下:memory=4GB 才是限制 WSL2 VM 的内存上限,processors=4 是限制逻辑处理器数量,和“释放内存”不是一回事。我的数值只是自己的配置,不建议照抄。

后边真正内存吃紧的时候,vmmemWSL 的 Working Set 只有一百多 MB,所以至少在那次现场里,WSL 不是主要嫌疑人。但这不能反过来证明“限制 4GB 给我省了多少内存”——只能说当时它没在吃。

然后被 1.7GB 非分页池带跑偏了

接着我把任务管理器的进程页、内存页截图丢给 Codex Desktop。它注意到非分页缓冲池有大约 1.7GB,于是后边花了不少时间沿着这个方向查。

最后没找到持续增长,也没定位到哪个驱动在泄漏。

现在回头看,这一步最大的问题不是“1.7GB 到底正不正常”,而是只有某一个时刻的截图,没有时间序列,就很难证明它是问题来源。一个数字看起来偏大,不等于它就是导致 90% 内存占用的罪魁祸首。

于是第二天重启之后,我改成了一个笨但有效的办法:等内存真不够的时候再抓现场。

让 PowerShell 在内存见底时抓现场

最开始用的脚本会每 5 秒看一次系统可用内存,低于 1GB 时,把进程按内存排出来。

不过后来发现原版有一个口径坑:它按 PrivateMB 排序。真正要看“现在有多少东西压在物理内存里”,WorkingMB 更有参考价值,所以整理到这里我把排序改成了 Working Set:

while ($true) {
    $m = Get-CimInstance Win32_PerfFormattedData_PerfOS_Memory

    if ($m.AvailableMBytes -lt 1000) {
        "`n$(Get-Date) Available=$($m.AvailableMBytes)MB"

        Get-Process -ErrorAction SilentlyContinue |
            Group-Object ProcessName |
            ForEach-Object {
                [pscustomobject]@{
                    Process = $_.Name
                    WorkingMB = [math]::Round(
                        ($_.Group | Measure-Object WorkingSet64 -Sum).Sum / 1MB
                    )
                    PrivateMB = [math]::Round(
                        ($_.Group | Measure-Object PrivateMemorySize64 -Sum).Sum / 1MB
                    )
                }
            } |
            Sort-Object WorkingMB -Descending |
            Select-Object -First 10 |
            Format-Table -AutoSize
    }

    Start-Sleep 5
}

这段只是读状态,一般不需要管理员权限。

然后我把自己每天签到和常用的页面全打开,合计 28 个 Chrome 标签页。可用内存很快掉到 600~800MB 左右,现场终于抓到了。

其中一次是这样:

ProcessWorkingMBPrivateMB
chrome73735090
msedgewebview213862130
ChatGPT12611093
svchost997605
Typora613543
MsMpEng400486
Telegram434430
PowerToys.PowerLauncher76379
NutstoreClient227366

这张表一下把前边很多猜测打回原形了。

尤其是 PowerToys Run:它的 PrivateMB 有 379MB,看着很吓人;但 WorkingMB 只有 76MB。所以“关掉 PowerToys Run 直接省 379MB”这种算法是不成立的。

当然我本来就几乎不用 Alt + Space,所以还是把它关了——不用的东西不常驻,总归没坏处。

WebView2 看着很大,但它不是一个软件

接下来最显眼的是 msedgewebview2

我第一反应也是:“我什么时候装过这个?”

后来才搞明白,WebView2 是很多 Windows 组件和第三方桌面应用拿来渲染网页式界面的运行时,而且它本身就是多进程模型。任务管理器里的一堆 msedgewebview2.exe,并不代表它们都属于同一个应用。

所以这里同样不能看到:

msedgewebview2  1.3GB Working Set

就直接得出:

Windows 小组件吃了 1.3GB

完全不是一回事。

真正想知道是谁在用,可以去任务管理器“详细信息”里把“命令行”列打开,或者看父进程,把 WebView2 进程重新归因到宿主应用。

而我的这次现场已经很直白了:Chrome 7GB+、WebView2 合计约 1.3GB、ChatGPT 1.2GB。 真正的大头明明都在自己开的应用上。

但我还是不死心:Windows 自带的东西能不能再抠一点?

到这里我又盯上了两个自己基本不用的东西:

  • Windows 11 Widgets / 小组件;
  • Windows Search 里的 Search Highlights、网页结果。

再加上 Delivery Optimization 的 P2P,我最后还是写了一个可恢复的 PowerShell 脚本做测试。

不过经历前边那轮之后,这版就没再走“见服务就禁”的路线了。思路只有三个:

  1. 只改策略,不卸系统组件;
  2. 不禁 SysMain、DiagTrack、Remote Registry、DoSvc 这些服务;
  3. 修改前记录原注册表状态,恢复时按原状态回滚,而不是猜 Windows 默认值。

传递优化也只是把 DODownloadMode 调到 0,也就是关掉 P2P;没有去禁用 DoSvc。

搜索策略在不同 Windows Edition / Build 上是否真正生效也有差异,所以这次我没把它包装成什么“Win11 通用一键优化”,就当成一次自己的机器上的实验。

第一次运行甚至还先被 PowerShell 执行策略拦了:脚本没数字签名,最后是在 PowerShell 里临时用了:

Set-ExecutionPolicy -Scope Process Bypass

这个作用域只影响当前 PowerShell 进程,窗口关掉就结束。

然后 Apply 正常执行,备份也成功写到了:

C:\ProgramData\WinMemoryTrim\backup.json

实测结果:好像降了?不,没区别

这是 Apply 之后的任务管理器截图,当时显示内存大约 61%

运行策略优化脚本后,任务管理器显示约 61% 内存占用

第一眼:“诶?是不是降下来了?”

然后我把恢复脚本也跑了一遍。之前新增的 Widgets、Search、Delivery Optimization 策略值全部按备份逻辑删掉 / 还原,再看任务管理器:

恢复脚本执行后,任务管理器显示约 59% 内存占用

59%。

哈哈哈哈。

恢复完反而还比“优化后”低 2 个百分点。

当然,这两张截图不是严格控制变量的 benchmark:后台进程在动、Defender 在动、缓存也在动,不能拿 61% 和 59% 得出“恢复还能省内存”这种反向结论。

但它至少足够推翻我一开始那个期待:

关掉 Widgets + 搜索联网 + 几个 Windows 服务,就能稳定释放几百 MB,甚至 1~2GB。

至少在我这台机器上,没有看到这种级别的收益。 两次粗测的差异连方向都不稳定,更像正常的后台波动。

而且恢复之后,Windows 小组件重新出现在任务管理器里也就一百多 MB 的量级。和 7~8GB 的 Chrome 放在一起,优先级已经很明显了。

所以到底什么对我有用?

折腾一圈之后,真正值得做的反而很朴素:

  • Chrome 开“内存节省程序”,我这种二三十个标签页的场景最有针对性;
  • 不用的标签页真的关掉——我知道,但我就是做不到🤣;
  • PowerToys Run 这种自己确实不用的常驻功能直接关;
  • 找出到底哪些应用在养着 WebView2,没必要常驻的就退出,而不是去删 WebView2 Runtime;
  • WSL / VMware 只在需要的时候开,或者给它们设合理上限;
  • 如果长期就是 Chrome + WSL2 + VMware + 一堆桌面应用一起跑,16GB 本身就是紧张,32GB 才是最直接的解决方案。

Widgets、Search 联网结果这些当然也可以按个人偏好关掉,但现在我更愿意把它们叫“去掉自己不用的功能”,而不是“内存优化”。

如果真想测,至少别只看一张任务管理器

这次我的测试本身也比较粗。如果以后真要认真比较某个优化到底有没有意义,我会按这种方式做:

重启
→ 空闲几分钟
→ 固定同一批应用 / 标签页
→ 记录 Available Memory + Working Set
→ 只改一个变量
→ 再重启 / 注销
→ 重复同样负载
→ 多跑几轮再比较

最重要的是一次只改一个东西,不然最后看到数字变化,也不知道是谁造成的。

另外,凡是会改注册表、服务启动类型或者卸系统组件的“优化脚本”,恢复逻辑最好先写、先测,再谈一键执行。尤其不要写死“恢复成 Automatic”之类的默认值——用户原来是什么状态,就应该恢复成什么状态。

最后

所以这次最大的收获,不是“找到了 Windows 偷吃的 2GB 内存”,而是把一个特别容易相信的故事完整跑了一遍:

内存高
→ Windows 后台垃圾太多
→ 关几个服务 / 小组件
→ 轻松释放 1~2GB

至少在我这台机器上,这条链不成立。

真正把内存吃掉的,就是我自己开的 Chrome、WebView2 宿主应用、ChatGPT,再叠 WSL / VMware 这种使用方式。Windows 自带那些东西有的确实可以关,但别指望它们替 16GB 内存续命。

从“Windows 内存释放大法”折腾到最后,结论变成了:

少开点东西,或者加内存。

好嘛,绕了一大圈又回到第一句话了🤣。

以上,下次见。

评论 →

CC BY-NC-SA 4.0

评论

评论由 GitHub Discussions 提供。登录 GitHub 后即可评论。 前往对应 Discussion