关键词:
一:背景
1. 讲故事
昨天有位朋友找到我,说他的程序内存存在泄露导致系统特别卡,大地址也开了,让我帮忙看一下怎么回事?今天上午看了下dump,感觉挺有意思,在我的分析之旅中此类问题也蛮少见,算是完善一下体系吧。
二:WinDbg 分析
1. 到底是哪里的泄露
在.NET高级调试训练营
中,我多次告诉学员们,在分析此类问题时一定要搞清楚是托管还是非托管的问题,否则就南辕北辙啦,接下来使用 !address -summary
观察下内存段。
0:000:x86> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
<unknown> 17673 cd777000 ( 3.210 GB) 83.76% 80.26%
Image 3087 1c14c000 ( 449.297 MB) 11.45% 10.97%
Free 1418 aae6000 ( 170.898 MB) 4.17%
Heap32 11 6ee0000 ( 110.875 MB) 2.82% 2.71%
Stack32 186 39c0000 ( 57.750 MB) 1.47% 1.41%
Stack64 186 f80000 ( 15.500 MB) 0.39% 0.38%
Other 24 1da000 ( 1.852 MB) 0.05% 0.05%
Heap64 4 190000 ( 1.562 MB) 0.04% 0.04%
TEB64 62 7c000 ( 496.000 kB) 0.01% 0.01%
TEB32 62 3e000 ( 248.000 kB) 0.01% 0.01%
Other32 1 1000 ( 4.000 kB) 0.00% 0.00%
PEB64 1 1000 ( 4.000 kB) 0.00% 0.00%
PEB32 1 1000 ( 4.000 kB) 0.00% 0.00%
--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE 16566 be8c2000 ( 2.977 GB) 77.67% 74.43%
MEM_IMAGE 4210 245be000 ( 581.742 MB) 14.82% 14.20%
MEM_MAPPED 421 12403000 ( 292.012 MB) 7.44% 7.13%
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_COMMIT 18901 e2904000 ( 3.540 GB) 92.36% 88.50%
MEM_RESERVE 2296 1297f000 ( 297.496 MB) 7.58% 7.26%
MEM_FREE 1519 ad6d000 ( 173.426 MB) 4.23%
仔细观察卦中信息:可以看到总的提交内存 MEM_COMMIT = 3.5G
都被 <unknown>=3.2G
这块给吃掉了,这表示当前是一个赤裸裸的 非托管内存泄露
,是某种代码通过 VirtualAlloc
这种方式直取 Windows内存管理器
内存,既然能用上 VirtualAlloc
肯定就不是业务程序员造成的,要想洞察 VirtualAlloc
的调用栈除了对程序安插监控和挂钩子,其余也没什么好办法了,仅仅从现有的 dump 中观察其实很难。
2. 真的没有希望吗
既然不知道是谁分配的,我们只能观察事发现场,或许能从内存现场中找到点答案,那怎么找事发现场呢?熟悉 Windows内存管理
的朋友都知道,要想分配内存,首先要在 虚拟地址
上分配一个内存段,然后将我们的数据放在内存段中的内存页上,所以思路就是找到所有 COMMIT
的 SEGMENT 即可,使用 !address -f:Unk,MEM_COMMIT
观察所有 Unk
的提交内存段。
0:000:x86> !address
BaseAddr EndAddr+1 RgnSize Type State Protect Usage
-----------------------------------------------------------------------------------------------
79530000 79550000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [................]
79550000 79570000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [................]
79580000 795a0000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [NatK............]
795a0000 795c0000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [........d.......]
795c0000 795e0000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [................]
79620000 79640000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [................]
79640000 79660000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [................]
79660000 79680000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [................]
79690000 796b0000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [................]
796c0000 796e0000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [..d.i.v...c.l.a.]
796e0000 79700000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [t...p.r.o.t.o.t.]
79700000 79720000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [t...p.r.o.t.o.t.]
79720000 79740000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [........8..z....]
79740000 79760000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [................]
79760000 79780000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [........d.......]
79780000 797a0000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [.....OP...vy....]
797a0000 797c0000 20000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [................]
797c0000 797c1000 1000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE <unknown> [U...E.....H..E.P]
797c1000 797e0000 1f000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [................]
797e0000 797e1000 1000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE <unknown> [U...E.....H..E.P]
797e1000 79800000 1f000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE <unknown> [................]
...
卦中信息太多了,刷了好久才刷完,Break 之后观察内存段,发现 RgnSize = 20000
的段貌似要多一些,为了发现 0x20000
的size到底有多少,这里需要写一段脚本进行统计,截图如下:
从卦中看 128k
的内存段个数就占用了 9000+
,应该是什么东西分配了内存但没有合理释放。
由于没有给程序装监控,只能看内存段的地址上的内容了,这里使用 windbg 自带的 .writemem
命令将内存写到文件中观察,简单观察之后,发现里面有很多的 js 代码以及 html,比如下面的 PopupCalendar
方法,截图如下:
3. 继续乘胜追击
既然抽到的几个内存段有这些 网页内容
,但不见得大部分内存段都有这些内容,那怎么去验证呢?可以使用 s
搜索内存去求证一下,比如我全内存搜索包含 PopupCalendar
的内存段有多少。
0:000:x86> s-a 0 L?0xffffffff "PopupCalendar"
08a4ed7c 50 6f 70 75 70 43 61 6c-65 6e 64 61 72 53 74 79 PopupCalendarSty
096f8fa3 50 6f 70 75 70 43 61 6c-65 6e 64 61 72 28 22 6f PopupCalendar("o
096f9026 50 6f 70 75 70 43 61 6c-65 6e 64 61 72 28 22 6f PopupCalendar("o
096ff5bb 50 6f 70 75 70 43 61 6c-65 6e 64 61 72 28 22 6f PopupCalendar("o
096ff63e 50 6f 70 75 70 43 61 6c-65 6e 64 61 72 28 22 6f PopupCalendar("o
...
f5fec996 50 6f 70 75 70 43 61 6c-65 6e 64 61 72 28 22 6f PopupCalendar("o
f5ff2f2b 50 6f 70 75 70 43 61 6c-65 6e 64 61 72 28 22 6f PopupCalendar("o
f5ff2fae 50 6f 70 75 70 43 61 6c-65 6e 64 61 72 28 22 6f PopupCalendar("o
f5ff9543 50 6f 70 75 70 43 61 6c-65 6e 64 61 72 28 22 6f PopupCalendar("o
f5ff95c6 50 6f 70 75 70 43 61 6c-65 6e 64 61 72 28 22 6f PopupCalendar("o
从内存地址看:PopupCalendar
从虚拟地址开头的 08a4ed7c
到虚拟地址快结束的 f5ff95c6
都遍布着这样的字符,而且高达 1532
处,这也就说明当前非托管内存中有大量的 html
页面被分配,但没有被释放,这也就是问题所在。
有了这些信息后接下来就找朋友反馈,为什么非托管内存中有这么多的 html 页面,是不是在 WPF 中不合理的使用了什么 浏览器引擎
,分配之后未合理释放导致的泄露?
三:总结
这次事故应该是第三方组件在使用 html 的方式上造成的泄露,把包围圈缩小到这里相信朋友能很快的找到问题,验证问题。
PS:在 dump 中寻找非托管内存泄露其实很多时候都比较绝望,需要你对 Windows内存管理
有一个比较深入的理解,还需要一个不骄不躁的分析心态。
记一次.net某家装erp系统内存暴涨分析(代码片段)
一:背景1.讲故事前段时间微信上有一位老朋友找到我,说他的程序跑着跑着内存会突然爆高,有时候会下去,有什么会下不去,怀疑是不是某些情况下存在内存泄露,让我帮忙分析一下,其实内存泄露方面的问题还是比较好解... 查看详情
记一次.net某电厂web系统内存泄漏分析
一:背景1.讲故事前段时间有位朋友找到我,说他的程序内存占用比较大,寻求如何解决,截图就不发了,分析下来我感觉除了程序本身的问题之外,.NET5在内存管理方面做的也不够好,所以有必要给大家分享一下。二:WinDbg分... 查看详情
记一次.net某智能服装智造系统内存泄漏分析(代码片段)
一:背景1.讲故事上个月有位朋友找到我,说他的程序出现了内存泄漏,不知道如何进一步分析,截图如下:朋友这段话已经说的非常言简意赅了,那就上windbg说话吧。二:Windbg分析1.到底是哪一方面的泄漏根据朋友描述,程序... 查看详情
记一次.net某家装erp内存暴涨分析(代码片段)
一:背景1.讲故事前段时间微信上有一位老朋友找到我,说他的程序跑着跑着内存会突然爆高,有时候会下去,有什么会下不去,怀疑是不是某些情况下存在内存泄露,让我帮忙分析一下,其实内存泄... 查看详情
记一次.net某消防物联网后台服务内存泄漏分析
一:背景1.讲故事去年十月份有位朋友从微信找到我,说他的程序内存要炸掉了。。。截图如下:时间有点久,图片都被清理了,不过有点讽刺的是,自己的程序本身就是做监控的,结果自己出了问题,太尴尬了 查看详情
记一次.net某消防物联网后台服务内存泄漏分析
一:背景1.讲故事去年十月份有位朋友从微信找到我,说他的程序内存要炸掉了。。。截图如下:时间有点久,图片都被清理了,不过有点讽刺的是,自己的程序本身就是做监控的,结果自己出了问题,太尴尬了 查看详情
记一次.net某wms仓储打单系统内存暴涨分析(代码片段)
一:背景1.讲故事七月中旬有一位朋友加wx求助,他的程序在生产上跑着跑着内存就飙起来了,貌似没有回头的趋势,询问如何解决,截图如下:和这位朋友聊下来,感觉像是自己在小县城当了个小老板... 查看详情
记一次.net某电厂web系统内存泄漏分析(代码片段)
一:背景1.讲故事前段时间有位朋友找到我,说他的程序内存占用比较大,寻求如何解决,截图就不发了,分析下来我感觉除了程序本身的问题之外,.NET5在内存管理方面做的也不够好,所以有必要给大... 查看详情
记一次.net某制造业mes系统崩溃分析(代码片段)
一:背景1.讲故事前段时间有位朋友微信找到我,说他的程序偶尔会出现内存溢出崩溃,让我帮忙看下是怎么回事,咨询了下程序是x86部署,听到这个词其实心里已经有了数,不管怎么样还是用windbg分析一... 查看详情
记一次java的内存泄露分析
当前环境jdk==1.8httpasyncclient==4.1.3代码地址git地址:https://github.com/jasonGeng88/java-network-programming背景前不久,上线了一个新项目,这个项目是一个压测系统,可以简单的看做通过回放词表(http请求数据),不断地向服务发送请求,... 查看详情
记一次.net某新能源系统线程疯涨分析
一:背景1.讲故事前段时间收到一个朋友的求助,说他的程序线程数疯涨,寻求如何解决。等我分析完之后,我觉得这个问题很有代表性,所以拿出来和大家分享下,还是上老工具WinDbg。二:WinDbg分析1.线程真的在疯涨吗要想查... 查看详情
记一次.net某外贸erp内存暴涨分析(代码片段)
一:背景1.讲故事上周有位朋友找到我,说他的API被多次调用后出现了内存暴涨,让我帮忙看下是怎么回事?看样子是有些担心,但也不是特别担心,那既然找到我,就给他分析一下吧。二:WinDbg分析1.到底是哪里的泄露这也是... 查看详情
记一次.net某桌面奇侠游戏非托管内存泄漏分析(代码片段)
一:背景1.讲故事说实话,这篇dump我本来是不准备上一篇文章来解读的,但它有两点深深的感动了我。无数次的听说用Unity可做游戏开发,但百闻不如一见。游戏中有很多金庸武侠小说才有的名字,太赏心悦目... 查看详情
记一次.net某电商定向爬虫内存碎片化分析(代码片段)
一:背景1.讲故事上个月有位朋友wx找到我,说他的程序存在内存泄漏问题,寻求如何解决?如下图所示:从截图中可以看出,这位朋友对windbg的操作还是有些熟悉的,可能缺乏一定的实操经验,所... 查看详情
记一次.net某智慧水厂api非托管内存泄漏分析(代码片段)
一:背景1.讲故事七月底的时候有位朋友在wx上找到我,说他的程序内存占用8G,托管才占用1.5G,询问剩下的内存哪里去了?截图如下:从求助内容看,这位朋友真的太客气了,动不动就谈钱,... 查看详情
02记一次netty内存泄露(代码片段)
前言最近在一个jt809的项目中碰到了这样的一个问题 最开始对方传输给我们的车辆数量不多的情况下,系统一直很稳健 但是由于之前又对接了大量的车辆,然后之后出现了一次OutOfDirectMemoryError,然后当时临时重启了一下 然后过了... 查看详情
记一次.net某上市工业智造cpu+内存+挂死三高分析(代码片段)
一:背景1.讲故事上个月有位朋友加wx告知他的程序有挂死现象,询问如何进一步分析,截图如下:看这位朋友还是有一定的分析基础,可能玩的少,缺乏一定的分析经验,当我简单分析之后,我发... 查看详情
记一次.net某新能源系统线程疯涨分析(代码片段)
一:背景1.讲故事前段时间收到一个朋友的求助,说他的程序线程数疯涨,寻求如何解决。等我分析完之后,我觉得这个问题很有代表性,所以拿出来和大家分享下,还是上老工具WinDbg。二:WinDbg分析1.... 查看详情