一:背景

1. 讲故事

前些天有位朋友找到我,说他们的程序有内存泄露,跟着我的错题集也没找出是什么原因,刚好手头上有一个 7G+ 的 dump,让我帮忙看下是怎么回事,既然找到我了那就给他看看吧,不过他的微信头像有点像 二道贩子,不管到我这里是不是 三道,该分析的还得要分析呀。

二:WinDbg 分析

1. 到底是哪里的泄露

这个非常简单,对用户态内存段做一个分组处理就明白了,可以用 windbg 的 !address -summary 观察便知。


0:000> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free 44571 7ffc`f0900000 ( 127.988 TB) 99.99%
<unknown> 77531 2`47cee000 ( 9.122 GB) 74.52% 0.01%
Heap 16406 0`a45cf000 ( 2.568 GB) 20.98% 0.00%
Image 2116 0`15ad7000 ( 346.840 MB) 2.77% 0.00%
Stack 2286 0`0d160000 ( 209.375 MB) 1.67% 0.00%
TEB 762 0`005f4000 ( 5.953 MB) 0.05% 0.00%
Other 39 0`00207000 ( 2.027 MB) 0.02% 0.00%
PEB 1 0`00001000 ( 4.000 kB) 0.00% 0.00% --- State Summary ---------------- RgnCount ----------- Total Size -------- %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
Number of GC Heaps: 1
generation 0 starts at 0x0000020F1BC03E80
generation 1 starts at 0x0000020F1AFE7BA0
generation 2 starts at 0x0000020D2E4B1000
ephemeral segment allocation context: none
Small object heap
segment begin allocated committed allocated size committed size
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 0xfffef10(268431120) 0xffff000(268431360)
0000020E54C60000 0000020E54C61000 0000020E64C60000 0000020E64C60000 0xffff000(268431360) 0xffff000(268431360)
0000020E9C050000 0000020E9C051000 0000020EAC04FF70 0000020EAC050000 0xfffef70(268431216) 0xffff000(268431360)
0000020ED89B0000 0000020ED89B1000 0000020EE89AFF20 0000020EE89B0000 0xfffef20(268431136) 0xffff000(268431360)
0000020F11FF0000 0000020F11FF1000 0000020F1C20E0A8 0000020F1C21A000 0xa21d0a8(169988264) 0xa229000(170037248)
Frozen object heap
segment begin allocated committed allocated size committed size
Large object heap starts at 0x0000020D3E4B1000
segment begin allocated committed allocated size committed size
0000020D3E4B0000 0000020D3E4B1000 0000020D460B3F00 0000020D460D4000 0x7c02f00(130035456) 0x7c23000(130166784)
0000020D72620000 0000020D72621000 0000020D7A19C808 0000020D7A19D000 0x7b7b808(129480712) 0x7b7c000(129482752)
0000020D7CFD0000 0000020D7CFD1000 0000020D84B0CDF0 0000020D84B2D000 0x7b3bdf0(129220080) 0x7b5c000(129351680)
0000020E08B90000 0000020E08B91000 0000020E0FAC4350 0000020E0FAC5000 0x6f33350(116601680) 0x6f34000(116604928)
0000020F2A040000 0000020F2A041000 0000020F2DB4A738 0000020F2DB4B000 0x3b09738(61904696) 0x3b0a000(61906944)
Total Allocated Size: Size: 0xabf0bd10 (2884680976) bytes.
Total Committed Size: Size: 0xabf5a000 (2885001216) bytes.
------------------------------
GC Allocated Heap Size: Size: 0xabf0bd10 (2884680976) bytes.
GC Committed Heap Size: Size: 0xabf5a000 (2885001216) bytes.

从卦中数据看当前托管堆也才 2.8G,这就比较懵逼了,看样子还是比较难搞的 非托管内存泄露,内存大概被 heap + VirtualAlloc/FileMap 合力吃掉了,接下来该怎么分析呢?有点迷茫。。。

2. 还有其他突破口吗

一般来说这种问题看dump效果已经很不好了,比较好的方式就是用 perfview 去监控 VirtualAlloc,HeapAlloc 分配,但现在可惜的是我们只有一个dump,怎么办呢? Windbg 命令除了可以看 ntheap,还可以看 clr 的私有堆,即 loader堆,或许有什么新的发现,可以使用命令 !eeheap -loader


0:000> !dumpdomain
--------------------------------------
System Domain: 00007fffeb742af0
...
--------------------------------------
Domain 1: 0000020d2c794430
Assembly: 0000020d59298350 [mr4vbdbg, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]
ClassLoader: 0000020D579778E0
SecurityDescriptor: 0000020D593DE320
Module
00007fff8f0a5af8 mr4vbdbg, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Assembly: 0000020d5751b040 [51buoqnx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]
ClassLoader: 0000020D57974130
SecurityDescriptor: 0000020D593E0060
Module
00007fff8f059798 51buoqnx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
...
Assembly: 0000020d5751c000 [r2bjpfrk, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]
ClassLoader: 0000020D579741E0
SecurityDescriptor: 0000020D593DF340
Module
00007fff8f05aff8 r2bjpfrk, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
...
Assembly: 0000020d5929acf0 [qgt1j2cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]
ClassLoader: 0000020D57976B20
SecurityDescriptor: 0000020D593DD6F0
Module
00007fff8f0a11d8 qgt1j2cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
...
Total size: Size: 0x0 (0) bytes.
--------------------------------------
Total LoaderHeap size: Size: 0x50f6d000 (1358352384) bytes total, 0x2f43000 (49557504) bytes wasted.
=======================================

我去,不看不知道,一看吓一跳,刷了好久也没刷完,,,看了下总大小是 1.35G,说明当前程序存在着程序集泄露,而且程序集的名字也是奇奇怪怪的比如上面的 r2bjpfrk , qgt1j2cs, 看样子都是动态生成出来的。

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

接下来抽几个 module 看看里面都定义了什么,使用 !dumpmodule -mt 命令即可。


0:000> !dumpmodule -mt 00007fff8f0a11d8
Name: qgt1j2cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
Attributes: PEFile
Types defined in this module MT TypeDef Name
------------------------------------------------------------------------------
00007fff8f0a1938 0x02000002 ServiceBase.WebService.DynamicWebLoad.xxxImplService Types referenced in this module MT TypeRef Name
------------------------------------------------------------------------------
00007fffc3232730 0x02000001 System.Web.Services.Protocols.SoapHttpClientProtocol
00007fffe81789e0 0x02000002 System.IAsyncResult
00007fffe81759d8 0x02000003 System.AsyncCallback
00007fffe15f42f8 0x02000004 System.Xml.Serialization.XmlElementAttribute
00007fffe57810a8 0x02000007 System.CodeDom.Compiler.GeneratedCodeAttribute
00007fffe80ee5f8 0x02000008 System.Diagnostics.DebuggerStepThroughAttribute
00007fffe5780210 0x02000009 System.ComponentModel.DesignerCategoryAttribute
00007fffc3239d60 0x0200000a System.Web.Services.WebServiceBindingAttribute
00007fffc323a2f8 0x0200000b System.Web.Services.Protocols.WebClientProtocol
00007fffc32322d8 0x0200000c System.Web.Services.Protocols.SoapDocumentMethodAttribute
00007fffe80f5dd8 0x0200000d System.Object
00007fffe80f59c0 0x0200000e System.String

经过一顿搜索,发现模块中都是这些内容,仔细分析 TypeDef NameTypeRef Name,大概就能猜测到,代码中有 SoapHttpClient 去访问这个 xxxImplService 类名的服务地址,有了这些信息就可以分析源码了。

3. 寻找源码

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

到这里就知道了来龙去脉,最后就是让朋友合理的去卸载这里的 程序集 ,或者干脆绕过去。

三:总结

这次事故主要是朋友用 CSharpCodeProvider 动态生成程序集导致的程序集泄露,猜测代码是在哪里 copy 过来的,一定要搞清楚原理才能放心用,合理创建合理释放。

记一次 .NET某新能源MES系统 非托管泄露的更多相关文章

  1. 记一次 .NET 某电子厂OA系统 非托管内存泄露分析

    一:背景 1.讲故事 这周有个朋友找到我,说他的程序出现了内存缓慢增长,没有回头的趋势,让我帮忙看下到底怎么回事,据朋友说这个问题已经困扰他快一周了,还是没能找到最终的问题,看样子这个问题比较刁钻,不 ...

  2. 记一次 .NET 某智慧水厂API 非托管内存泄漏分析

    一:背景 1. 讲故事 七月底的时候有位朋友在wx上找到我,说他的程序内存占用8G,托管才占用1.5G,询问剩下的内存哪里去了?截图如下: 从求助内容看,这位朋友真的太客气了,动不动就谈钱,真伤感情, ...

  3. 记一次 WinDbg 分析 .NET 某工厂MES系统 内存泄漏分析

    一:背景 1. 讲故事 上个月有位朋友加微信求助,说他的程序跑着跑着就内存爆掉了,寻求如何解决,截图如下: 从聊天内容看,这位朋友压力还是蛮大的,话说这貌似是我分析的第三个 MES 系统了,看样子 . ...

  4. 【详解】ERP、APS与MES系统是什么?

    ERP是什么?MES是什么?APS又是什么?无论他们有什么功能,对企业有什么意义,不过都是计算机在读写一些数据而已.实际上这一切的本质不过是数据在硬盘和内存中快速的读和写. ERP是--,APS是-- ...

  5. MES系统学习

    MES系统是当今制造型企业信息化的热点,而统一建模语言UML是面向对象建模的标准语言,在软件工程发挥着重要作用.MES系统如何进行UML建模呢,今天和大家重点讨论一下MES系统的UML建模方法,请看本 ...

  6. 浅谈MES系统SMT的JIT功能(一):JIT原理

    前段时间帮忙客户实现了MES系统的SMT线上的JIT功能(JIT功能只适合电子行业的生产线),今天就来谈谈JIT功能是什么,为什么工厂车间需要用到JIT等等一些经验 首先说说JIT: 准时制生产方式( ...

  7. 听说你的MES系统又失败了?

    前些日子,一位前同事跟我抱怨,他们做的MES系统,凉凉了.这样的话,我从不同人口中听到过不止一次. 我们做的系统,做到一半做不下去了...... 我们的系统,工人都不爱用...... 不只是MES,所 ...

  8. C#C/S框架演示 (MES系统)

    之前做过一个MES系统,发一些里面的截图.如果有朋友也用这个框架.或者有兴趣可以一起学习学习.使用开发工具VS2013,数据库SqlServer2008和Oracle11C.插件dev15.2,开发模 ...

  9. ERP系统和MES系统的区别

    公司说最近要上一套erp系统,说让我比较一下,erp系统哪个好,还有mes系统,我们适合上哪个系统,其实我还真的不太懂,刚接触erp跟mes的时候,对于两者的概念总是傻傻分不清楚,总是觉得既然都是为企 ...

  10. MES系统在小批量电子行业生产管理中的应用

    小批量电子产品生产管理的主要问题 电子电器制造类企业,既有单件小批量生产,也有批量生产:有按库存生产,也有按订单生产,属于典型的离散制造行业.因产品的不同其生产工艺流程也不尽相同,生产设备的布置不是按 ...

随机推荐

  1. Custom directive is missing corresponding SSR transform and will be ignored

    背景 最近在给业务组件库集成指令库,将各个项目中常用的指令如一键复制.元素和弹窗拖拽等封装到一起,进行统一发版维护. 业务组件库项目架构采用的是pnpm+vite+vue3+vitepress,其中v ...

  2. in用不用索引,啥时候能用啥时候不能用,一文说清

    in/or到底能不能用索引应该是肯定的,但有时生效有时不生效,这个能不能量化计算?这是本文想讨论和解答的问题. in到底用不用索引感觉像一桩悬疑片!古早时期的面经,统一说不走索引,在一些程序员脑海中从 ...

  3. React后台管理系统10 菜单数据的整理、以及其余路径的配置、刷新时默认当前选中样式

    对菜单进行数据整理 import { DesktopOutlined, FileOutlined, PieChartOutlined, TeamOutlined, UserOutlined, } fr ...

  4. .Net Core 如何数据导出 Excel?(EPPlus->OfficeOpenXml 实现固定列和动态列导出)

    〇.前言 对于将数据以 Excel 表格文件输出,还是比较常用的,也存在诸多情况,比如列固定或不固定.数据类型为 List<T>或 Json 对象等. 本文通过包 OfficeOpenXm ...

  5. 聊一聊Java中的Steam流

    1 引言 在我们的日常编程任务中,对于集合的制造和处理是必不可少的.当我们需要对于集合进行分组或查找的操作时,需要用迭代器对于集合进行操作,而当我们需要处理的数据量很大的时候,为了提高性能,就需要使用 ...

  6. ASP.NET Core - 日志记录系统(一)

    一.日志记录 日志记录是什么?简单而言,就是通过一些方式记录应用程序运行中的某一时刻的状态,保留应用程序当时的信息.这对于我们进行应用程序的分析.审计以及维护有很大的作用. 作为程序员,我们恐怕谁也不 ...

  7. 反射(Java Reflection)

    Java反射机制概述 Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性及方法. ...

  8. Easygraph:全面高效的图分析与社会计算开源工具

    前言图是对事物之间关系的一种原生的表达,利用图可以深入直接地认识世界中的关联.社交网络.交易数据.知识图谱.交通运输.生物技术等都是图数据的典型应用.社交网络是一种特殊的图数据,它建立在图网络的基础上 ...

  9. Cilium系列-5-Cilium替换KubeProxy

    系列文章 Cilium 系列文章 前言 将 Kubernetes 的 CNI 从其他组件切换为 Cilium, 已经可以有效地提升网络的性能. 但是通过对 Cilium 不同模式的切换/功能的启用, ...

  10. 【go语言】2.3.1 错误处理的基本概念

    在 Go 语言中,错误处理是通过返回错误值进行的,而不是像一些其他语言那样通过抛出和捕获异常.Go 语言有一个内置的接口类型 error,专门用于处理错误. error 接口 error 是一个内置的 ...