现象
点开 设置 → 网络和 Internet → 数据使用量,页面转圈,然后白屏无响应,得用任务管理器结束"设置"进程才能关掉。同一台机器上别的设置页都正常,只有这一页必卡,而且是越用越卡:一开始只是慢几秒,后来直接打不开。
原因分析
第一个环:手机热点每次都在换"身份"
排查是从为什么会有这么多网络开始的。我导出了注册表里 Windows 记住的所有网络:
powershell
reg export "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles" ...
结果这一项就是 1097 个。其中绝大多数名字都叫 Redmi K70 969 ------ 也就是那台手机的热点。
问题出在 Windows 判断"是不是同一个网络"的方式。它不只看 SSID,而是看 SSID + 网关 MAC + DNS 后缀 这一组特征。而 Android 的热点默认开着 MAC 随机化,每开一次热点,网关 MAC 就换一个。SSID 永远叫 Redmi K70 969,身份却每次都不同。
我把 969 个 Redmi 档案的网关 MAC 提取出来去重:940 个不同的 MAC ,其中 99.7% 是本地管理位置 1 的随机化地址。另一组 NCUWLAN 也是 50 个档案对 33 个 MAC。
证据在 Signature 那一侧,Windows 用 SSID 和 MAC 分别建签名,两边的数量对得上:
| 位置 | 项数 |
|---|---|
NetworkList\Profiles |
1097 |
NetworkList\Signatures\Unmanaged |
1097 |
每连一次热点,Windows 就认成一个从没见过的新网络,老老实实新建一份配置档案。
第二个环:档案把 SRUM 数据库撑爆了
这才是真正压死页面的东西。数据使用量页面读的不是网络档案,是 SRUM,库文件在:
makefile
C:\Windows\System32\sru\SRUDB.dat
它是一个 ESE(Extensible Storage Engine)数据库,也就是 Exchange 和 AD 用的那套嵌入式引擎。表名是 GUID,不是人能读的名字。我用 Python 直接解析了这张库(native P/Invoke 调 Jet API 读的,官方那套托管封装在这个库上读不动),网络用量表对应的是这个 GUID:
{973F5D5C-1D90-4944-BE8E-24B94231A174}
里面的关键字段是 AppId、UserId、L2ProfileId、BytesSent、BytesRecvd。L2ProfileId 就是网络档案的 ID ------ 每个档案每应用每小时一行。
打开库之后数字很难看:
ini
SRUDB.dat = 161.31 MB
网络用量表 = 99,601 行
涉及 AppId ≈ 40,000 个
还有一张 SruDbIdMapTable,把 AppId 反查成进程路径或服务名。约 4 万个 AppId 挤在这张表里,而它本身也占空间。
第三个环:卡死是乘法,不是加法
这一点是排查里最关键的判断。数据使用量页面要做的事是:遍历网络用量表 → 按应用聚合 → 对每个 AppId 去 SruDbIdMapTable 查一次名字 → 排序 → 渲染。档案数决定了聚合的维度,AppId 数决定了查名字的次数,两件事是乘在一起的。
1097 个档案 × 4 万 AppId 量级的映射表,再压在一个 161 MB 的 ESE 库上,单次查询要几十秒。设置页那个列表是 UI 线程现算现排的,等不到结果就转圈、假死。
我在外面用程序读这个库时,复现过一模一样的症状 ------ 同样的操作,库小的时候秒回,库涨到这个体积就卡住不动。所以这不是设置应用的 bug,是它老老实实在算一个被撑大了两个数量级的查询。
顺带说一句,同一个根因还干过另一件更贵的事:每个"新网络"都被当成全新的、非计量的 WiFi ,Windows 判断"这是计量网络,先别下载"的依据每次都重置,后台更新被反复重新放行。我核对过 DefaultMediaCost 下的 WiFi 是 1(非计量)。这一条跟卡死是两个症状、一个病根,但本文只讲卡死,流量的事另说。
解决
思路很直接:档案和库,各断一个环。 先把输入源清掉,再把已经膨胀的库重建。
一、备份
动注册表之前先整键导出。这里踩了个小坑,PowerShell 直接调 reg export 会因为参数顺序报错,得走 cmd /c:
powershell
cmd /c "reg export \"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\" \"$backup\" /y"
导出文件 2280.1 KB,留着随时能还原。
二、清掉僵尸档案(1097 → 67)
生成删除清单时保留了当前在用的那个档案和系统自带的保留项,其余按签名配对删除:
| 项 | 清理前 | 清理后 |
|---|---|---|
NetworkList\Profiles |
1097 | 67 |
NetworkList\Signatures\Unmanaged |
1097 | 69 |
1030 个档案全部删除成功,零失败。
删的时候有个容易白干的点:注册表路径前缀写 Microsoft.PowerShell.Core\Registry:: 会一个都删不掉,得写成 Registry::HKEY_LOCAL_MACHINE\...。我第一次就是这么空跑了一遍,清完还是 1097。
三、重建 SRUM(161.31 MB → 1.1 MB)
SRUDB.dat 被 DPS 服务占着,普通复制打不开,得用 VSS 快照把它捞出来:
powershell
esentutl /y C:\Windows\System32\sru\SRUDB.dat /vss /d <目标路径>
重建前先把整个 C:\Windows\System32\sru 目录备份走(14 个文件,162 MB),旧的 SRUDB.dat 归档为 *.old_20260913_183842,后来挪出了 C 盘。重建后库里只剩约 1.1 MB,按小时继续写。
中间有个插曲:清理时停 ndu 服务,内核驱动卡在 STOP_PENDING 下不来了,sc start 报 1056、sc control 报 1052。这种状态没法在运行时救,重启即可 ------ ndu 是 AUTO_START,重启后确认 STATE : 4 RUNNING。
四、结果
重启之后设置页打开恢复正常,秒开。网络功能也复验过,HTTPS 请求正常返回 200,重启后 Redmi K70 那个档案被重新建了一份,不带数字后缀。
五、这一步不做,几个月后复发
上面两步是把已经烂掉的东西清掉,但产生垃圾的机制还在。只要手机热点继续随机化 MAC,Windows 就会继续把每次连接当成新网络,档案会重新长回来,SRUDB 也会重新膨胀。所以真正的收尾是关掉随机化:
手机端(根源):设置 → 便携式热点 → 关掉"使用随机 MAC"/"随机化 MAC 地址"。网关 MAC 固定之后,Windows 永远认这一个网络,不再新建档案。
电脑端 (兜底):把 DefaultMediaCost 下的 WiFi 改成 2(计量),这样即使档案重建,Windows 也不会再把新网络当成"随便下"的非计量网络。