当前位置:编程学堂 > 记一次.NET新能源MES系统非托管泄漏

记一次.NET新能源MES系统非托管泄漏

  • 发布:2023-10-09 21:16

1:背景

1。讲故事

前几天,一个朋友来找我,说他们的程序出现了内存泄漏。按照我的错误问题集后,我找不到原因。我手头正好有一个7G+的dump,让我帮忙看看到底是怎么回事。反正你既然找到了我,就让他看看吧,不过他的微信头像有点像二道庄家。不管他来找我是不是三道,都需要分析。需要分析一下。 😄😄😄

二:WinDbg 分析

1。哪里漏了

这很简单。您可以通过对用户模式内存段进行分组来理解它。可以使用windbg的!address -summary来观察。


0:000> !地址摘要
--- 使用摘要 ---------------- RgnCount ----------- 总大小 -------- %ofBusy %ofTotal
免费 44571 7ffc`f0900000 (127.988 TB) 99.99%
 77531 2`47cee000 (9.122 GB) 74.52% 0.01%
堆 16406 0`a45cf000 (2.568 GB) 20.98% 0.00%
图片 2116 0`15ad7000 ( 346.840 MB) 2.77% 0.00%
堆栈 2286 0`0d160000 ( 209.375 MB) 1.67% 0.00%TEB 762 0`005f4000 (5.953 MB) 0.05% 0.00%
其他 39 0`00207000 (2.027 MB) 0.02% 0.00%
PEB 1 0`00001000 (4.000 kB) 0.00% 0.00%

--- 状态摘要 ---------------- RgnCount ----------- 总大小 -------- %ofBusy %ofTotal
MEM_FREE 44571 7ffc`f0900000 (127.988 TB) 99.99%
MEM_COMMIT 86575 1`e571e000 (7.585 GB) 61.96% 0.01%
MEM_RESERVE 12566 1`29fd2000 (4.656 GB) 38.04% 0.00%

从六角图中可以清楚地看到MEM_COMMIT=7.5G,同时Heap=2.5G,说明可能存在托管内存泄漏。接下来使用 !eeheap -gc 查看托管堆内存。


0:000> !eeheap -gc
GC 堆数量:1
第 0 代从 0x0000020F1BC03E80 开始
第 1 代从 0x0000020F1AFE7BA0 开始
第 2 代从 0x0000020D2E4B1000 开始临时段分配上下文:无
小对象堆
         段开始分配已提交已分配大小已提交大小
0000020D2E4B0000 0000020D2E4B1000 0000020D3E4B0000 0000020D3E4B0000 0xffff000(268431360) 0xffff000(268431360)
0000020D09830000 0000020D09831000 0000020D1982FFE0 0000020D19830000 0xfffefe0(268431328) 0xffff000(268431360)
0000020D9D9E0000 0000020D9D9E1000 0000020DAD9DFFB8 0000020DAD9E0000 0xfffefb8(268431288) 0xffff000(268431360)
0000020DD50C0000 0000020DD50C1000 0000020DE50BFFB0 0000020DE50C0000 0xfffefb0(268431280) 0xffff000(268431360)
0000020E10B90000 0000020E10B91000 0000020E20B8FF10 0000020E20B90000 0xffff10(268431120) 0xffff000(268431360)
0000020E54C60000 0000020E54C61000 0000020E64C60000 0000020E64C60000 0xffff000(268431360) 0xffff000(268431) )0000020D7CFD0000 0000020D7CFD1000 0000020D84B0CDF0 0000020D84B2D000 0x7b3bdf0(129220080) 0x7b5c000(129351680)
0000020E08B90000 0000020E08B91000 0000020E0FAC4350 0000020E0FAC5000 0x6f33350(116601680) 0x6f34000(116604928)
0000020F2A040000 0000020F2A041000 0000020F2DB4A738 0000020F2DB4B000 0x3b09738(61904696) 0x3b0a000(61906944)
总分配大小:大小:0xabf0bd10 (2884680976) 字节。
总提交大小:大小:0xabf5a000 (2885001216) 字节。
----------------------------------
GC 分配的堆大小:大小:0xabf0bd10 (2884680976) 字节。
GC 提交的堆大小:大小:0xabf5a000 (2885001216) 字节。


从六角图中的数据可以看出,当前托管堆只有2.8G,相当混乱。处理非托管内存泄漏好像比较难,内存大概是heap + VirtualAlloc/FileMap一起吃,接下来怎么分析?有点困惑。 。 。

2。还有其他突破吗

一般来说,这类问题的dump效果很差。更好的方法是使用 perfview 来监视 VirtualAlloc 和 HeapAlloc 分配。但不幸的是现在我们只有一个转储。我们应该做什么? Windbg命令除了看ntheap之外,还可以看clr的私有堆,即loader堆。如果你可能有什么新的发现,可以使用命令!eeheap -loader


0:000> !dumpdomain
-----------------------------------------------------------
系统域:00007fffeb742af0
...
-----------------------------------------------------------
域名 1: 0000020d2c794430
程序集:0000020d59298350 [mr4vbdbg,版本=0.0.0.0,文化=中性,PublicKeyToken=null]
类加载器:0000020D579778E0
安全描述符:0000020D593DE320
  模块
  00007fff8f0a5af8 mr4vbdbg,版本=0.0.0.0,文化=中性,PublicKeyToken=null

程序集:0000020d5751b040 [51buoqnx,版本=0.0.0.0,文化=中性,PublicKeyToken=null]
类加载器:0000020D57974130
安全描述符:0000020D593E0060
  模块
  00007fff8f059798 51buoqnx,版本=0.0.0.0,文化=中性,PublicKeyToken=null
...程序集:0000020d5751c000 [r2bjpfrk,版本=0.0.0.0,文化=中性,PublicKeyToken=null]
类加载器:0000020D579741E0
安全描述符:0000020D593DF340
  模块
  00007fff8f05aff8 r2bjpfrk,版本=0.0.0.0,文化=中性,PublicKeyToken=null
...
程序集:0000020d5929acf0 [qgt1j2cs,版本=0.0.0.0,文化=中性,PublicKeyToken=null]
类加载器:0000020D57976B20
安全描述符:0000020D593DD6F0
  模块
  00007fff8f0a11d8 qgt1j2cs,版本=0.0.0.0,文化=中性,PublicKeyToken=null
...
总大小:大小:0x0 (0) 字节。
-----------------------------------------------------------
LoaderHeap 总大小:大小:总共 0x50f6d000 (1358352384) 字节,浪费了 0x2f43000 (49557504) 字节。
=========================================


去了不看也不知道。我一看就震惊了。我花了很长时间才完成。我检查了一下,总大小为1.35G,这意味着当前程序存在程序集泄漏。 ,而且程序集的名字也很奇怪,比如上面的r2bjpfrk和qgt1j2cs,看起来都是动态生成的。

这里提醒一下,不要看1.35G,它还会涉及到其他不关联的内存,比如VirtualAlloc/MapFile/GCHeap等。等等

接下来,拿几个模块来看看其中定义了什么。使用 !dumpmodule -mt 命令。


0:000> !dumpmodule -mt 00007fff8f0a11d8
名称:qgt1j2cs,版本=0.0.0.0,文化=中性,PublicKeyToken=null
属性:PE文件
本模块中定义的类型

              MT 类型定义名称
-------------------------------------------------- ----------------------------
00007fff8f0a1938 0x02000002 ServiceBase.WebService.DynamicWebLoad.xxxImplService

本模块中引用的类型

              MT 类型参考名称
-------------------------------------------------- ----------------------------
00007fffc3232730 0x02000001 系统.Web.Services.Protocols.SoapHttpClientProtocol
00007fffe81789e0 0x02000002 系统.IAsyncResult
00007fffe81759d8 0x02000003 系统.AsyncCallback
00007fffe15f42f8 0x02000004 System.Xml.Serialization.XmlElementAttribute00007fffe57810a8 0x02000007 系统.CodeDom.Compiler.GenerateCodeAttribute
00007fffe80ee5f8 0x02000008 系统.诊断.DebuggerStepThroughAttribute
00007fffe5780210 0x02000009 系统.www.sychzs.cnerCategoryAttribute
00007fffc3239d60 0x0200000a 系统.Web.Services.WebServiceBindingAttribute
00007fffc323a2f8 0x0200000b 系统.Web.Services.Protocols.WebClientProtocol
00007fffc32322d8 0x0200000c 系统.Web.Services.Protocols.SoapDocumentMethodAttribute
00007fffe80f5dd8 0x0200000d 系统对象
00007fffe80f59c0 0x0200000e 系统字符串

经过一番查找,发现这些内容都在模块里。仔细分析TypeDef NameTypeRef Name,我大概可以猜到代码中有SoapHttpClient。 转到这个xxxImplService类名的服务地址。有了这些信息,您就可以分析源代码。

3。找到源代码

很快就找到了代码。原来是在请求WebService的过程中使用CSharpCodeProvider动态生成了程序集,并塞了4个dll。截图如下:

现在您知道来龙去脉了。最后,你可以请你的朋友合理卸载这里的程序集,或者干脆绕过它。

三:总结

这次事故主要是由于有朋友使用CSharpCodeProvider动态生成程序集导致的程序集泄露。我猜到代码被复制到哪里了。必须了解原理,才能放心使用,并合理创建和发布。

相关文章