windows内存泄露四则

比较稳定得干了这么多年,谁能想到一年内能处理4起内存泄露问题呢?


第一则比较简单,运营人员操作时遭遇经典弹窗《计算机的内存不足》。打开任务管理查看,未发现高占用的应用程序。

找运维获得管理员账户,打开任务管理器,发现亚信安全杀毒软件进程占用内存最多。运维通过管理后台卸载杀毒软件后恢复正常。

问题反馈软件厂家,厂家调查回复,用户登录时会在右下角弹出软件运行状态窗口,该窗口存在内存泄露问题,假如用户未关闭该窗口,则会一直泄露内存,最终导致内存不足。

因为该问题发生多次,(服务器会有很多登录后不管的session),部分服务器由运维统一卸载该软件以解决问题。


第二则调查非常困难,费时良久,累计工时超过5天。

2024/01/10 发现服务器 A 运行程序警告内存不足。调查发现无用户进程占用较高,任务管理器显示存在有 72G 内存是已缓存。
2024/01/11 重启服务器 A,内存泄露原因暂时认定为亚信杀毒软件卸载后残留驻留驱动未重启卸载。
2024/01/24 又发现服务器 A 运行程序警告内存不足。调查发现无用户进程占用较高。
2024/01/25 调查发现 NetSignCNGGuardService.exe 进程造成内存泄露。

各工具无法发现问题进程,开始深入调查。

Rammap 工具对 mapper 文件进行排序,最大仅为 60M。

系统内核内存占用显示占用内存最大的系统驱动仅占用 630MB:

对整个系统内存 dump 后用 windbg 进行分析。所有内存池中分页内存和不可分页内存合计仅 6.8G+9.6G=16.4G:

运行 !memusage,对所有内存分析后,其中 Modified 与 Dirty 同为 74.8G:

列出内存页帧 PFN 共 66759 个,全为 Page File Section,其中 Dirty 加起来为 63G:

使用工具统计系统全部句柄,其中 Section 最多,为 542773 个:

查看任务管理器,按句柄排序,最大进程为 2800 NetSignCNGGuardService.exe,共 520788 个句柄,同时产生大量页面错误。

使用工具分析 2800 进程的句柄,其中 Section 为 520679 个。

使用工具分析 2800 进程的句柄,其中 Section 大小基本为 144k,144k*520679=71.5G:

查看多个 section 对象,均发现目标内存地址损坏,但占用内存大小为 0x24000 即 144k:

该程序是北京信安世纪的证书认证服务,重启该服务后,内存释放。将问题反馈厂家和上级主管部门,回复表示不会修复该问题。后配置计划任务每周重启一次,但问题经过一段时间后内存泄露更加严重,现配置每天重启一次。


第三则依旧是亚信安全杀毒软件

2024/03/08 技术同事 X 发现服务器 B 的任务管理器中已缓存内存 >70G。同时发现其余多台机器都存在该问题。
检查发现原因是打开文件过多,使用 Rammap 工具清理后,内存释放。
因文件句柄无法导出持有进程信息,可以基本认定为亚信安全杀毒软件造成。已卸载亚信安全的服务器没有已缓存内存占用高的现象。

已缓存内存 112GB,已申请内存 129GB。

Rammap 工具显示内存页信息大多为日志文件映射。文件详情中 99% 为日志文件,且没有打开进程信息。

文件数量 8330,按单文件 100MB 计算,打开的文件占内存 >800GB。(实际日志文件应不到1000个)

怀疑亚信安全杀毒软件监听扫描文件后没有关闭文件 handle,导致 handle 泄露。由运维卸载软件后解决该问题。某运维领导因收取巨额回扣被全司通报并开除处理。(用信创享大福)


第四则是第三则问题产生的副作用。运营人员操作时遭遇经典弹窗《页面文件太小》。

对整个系统内存 dump 后用 windbg 进行分析。运行 !memusage 给出的数据并没有特别值得注意的条目,运行 !vm 给出的信息则比较有用。

重启这些程序后,虚拟内存得到释放。

主要原因在于 windows 虚拟内存这个功能,以及任务管理器条目说明的误解。

在上古时期,内存还在 xMB 的时代,随便设一个虚拟内存都能比物理内存大,但现在服务器上的物理内存比 c 盘都大,理论上可以不配置虚拟内存。但因为历史惯性问题,运维依旧会配置一个虚拟内存,比如说该服务器虚拟内存大小仅配置了 16G,远小于物理内存 128G,发生了内存大小倒挂。(其中微软推荐将虚拟内存大小设置为物理内存大小的 1.5 倍)

windows 的虚拟内存又称页面文件相当于 linux 的 swap,但实际作用比 swap 更加复杂一点。比如windows 会把打开的文件映射到虚拟内存中,还会包括别的一些 handle。那当虚拟内存被占满时,windows 无法打开更多的文件,自然也无法打开更多程序,这时系统则会提示《页面文件太小》。

那为什么虚拟内存会被占满呢?原因竟是我介绍了 Rammap 这个工具,各同事热衷于使用该工具的释放内存功能,该功能能让各进程吐还物理内存。这里就触发了 windows 的一个 feature,因为虚拟内存本身设计是供系统物理内存不足时使用的,Rammap 的释放内存功能让各进程把占用的部分物理内存转移到了虚拟内存上,反而导致了虚拟内存不足。

这种情况下,windows 只能通过结束进程来释放内存。而 linux 下可以重新把 swap 重新换入物理内存。

使用任务管理器的详情页面可以更快的发现问题原因。但在任务管理器中有一些条目会产生一些误解,特别是 win7 升级到 win8 以后,任务管理器的页面被重写了。其中有一些知识如下:

物理内存=使用中+可用
已提交=使用中的物理内存+使用中的虚拟内存

那么在详情中选择按提交大小排序,简单心算 (提交大小 – 工作集) 则可快速找到占用虚拟内存较大的进程。