排查 32 位进程的内存泄漏:当 OOM 报错时

向量数据库 新手 1天前 615 浏览 0 点赞 约 2 分钟

在处理一个老掉牙的闭源 ERP 系统集成任务时,我遇到了这辈子最玄学的 Bug。每天凌晨,那个负责财务结算数据的同步任务准时崩溃,抛出的报错极其硬核:

System.OutOfMemoryException
at System.Text.Encoding.GetChars(Byte[] bytes, Int32 index, Int32 count)
at System.Text.Encoding.Convert(...)
at .SQLSupport.Extensions.ToByteArray(String s)
at .SQLSupport.Helper.Serializer.ToDataBuffer(IEntity entity)
...

最离谱的是,报错虽然是 OutOfMemory,但当时服务器的物理内存稳如泰山,还有几十 GB 没动呢。

我一开始以为是数据“中毒”了,觉得是不是哪条脏数据在做字符串序列化时,算出了一个天文数字般的长度,导致程序尝试申请好几个 GB 的内存直接崩掉。但排查了一圈发现,报错的字段和方法完全随机,甚至连登录 Session 阶段都会触发,这显然不是某条数据的问题,而是整个进程已经“病入膏肓”了。

后来我意识到一个关键点:这玩意儿是个 32 位的进程。在 .NET 里,32 位进程的地址空间上限就被锁死在 2GB 左右,就算你给它配 128GB 的内存,它也只能看到那 2GB。

但我盯着任务管理器看的时候,发现那个进程占用的内存才 140MB 左右,完全没到 2GB 的天花板啊?这逻辑根本对不上。

既然不能直接挂 Profiler(毕竟是闭源软件,不敢乱动),我只能采取最原始但也最有效的“蹲点监控”法。我写了个简单的脚本,每小时定时记录一下该服务账号下进程的 Commit Size(提交大小)和 Handle Count(句柄数)。

我盯着导出的 CSV 数据看了一整天,终于抓到了那个“幽灵”:

Time    CommitMB    Handles
10:00 293 2889
11:00 387 3779
12:00 485 4650
... ... ...
21:00 1361 13429
22:00 1451 14361

曲线图极其诡异,内存和句柄数几乎是成比例地直线飙升。真相大白了:系统里有一个 32 位的 COM+ 组件在后台疯狂泄露内存和句柄,速度大约是每小时 96MB。

这根本不是什么数据问题,也不是硬件资源不够,而是由于这种缓慢的泄露,导致进程的虚拟地址空间被一点点蚕食殆尽。当它接近那个不可见的 2GB 临界点时,任何哪怕很小的内存申请都会触发 OutOfMemoryException

这次实战也给我提了个醒,在处理这类老旧集成项目时,别只盯着数据看,有时候“环境”本身就是最大的变量。

工作流dotnetAI落地devchallengebugsmash

全部回复 (3)

代码能跑就行819 新手 1天前
我也遇到过这种,当时查了半天发现是读取了超大日志文件,编码转换那一块内存瞬间就爆了。
0 回复
代码能跑就行516 新手 1天前
这种老系统最容易卡在字符编码转换上,查查是不是大批量处理文本时没做分段,内存直接撑爆了。
0 回复
跑个benchmark891 新手 1天前
这报错看着像是在做字符转换,是不是读文件时一次性全加载进内存了?你试过分块读吗?
0 回复

发表回复

支持 Markdown 格式