记一次 .NET某报关系统 非托管泄露分析

这篇具有很好参考价值的文章主要介绍了记一次 .NET某报关系统 非托管泄露分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一:背景

1. 讲故事

前段时间有位朋友找到我,说他的程序内存会出现暴涨,让我看下是怎么事情?而且还告诉我是在 Linux 环境下,说实话在Linux上分析.NET程序难度会很大,难度大的原因在于Linux上的各种开源工具主要是针对 C/C++, 和 .NET 一毛钱关系都没有,说到底微软在 Linux 上的调试领域支持度还远远不够。

虽然知道分析起来难度可能会很大,但该分析还是要分析的,让朋友抓一个 dump 过来,上 WinDbg 说话。

二:WinDbg 分析

1. 到底是哪里的泄露

只要是进程都会有内存段的,所以分析Linux的dump一样可以使用 !address -summary 命令来观察。


0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
<unknown>                              1607 ffffffff`cd7a9e00 (  16.000 EB) 100.00%  100.00%
Image                                 41699        0`31e57200 ( 798.340 MB)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
                                       1247 fffffffe`1c910000 (  16.000 EB)          100.00%
MEM_PRIVATE                           42059        1`e2cf1000 (   7.544 GB)   0.00%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
                                       1247 fffffffe`1c910000 (  16.000 EB) 100.00%  100.00%
MEM_COMMIT                            42059        1`e2cf1000 (   7.544 GB)   0.00%    0.00%

--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE                        41067        1`cff54000 (   7.249 GB)   0.00%    0.00%
PAGE_READONLY                           644        0`07268000 ( 114.406 MB)   0.00%    0.00%
PAGE_EXECUTE_READ                       223        0`06d1f000 ( 109.121 MB)   0.00%    0.00%
PAGE_EXECUTE_WRITECOPY                  125        0`04e16000 (  78.086 MB)   0.00%    0.00%

--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
<unknown>                              7ffc`78f8e000 ffff8003`86672000 (  16.000 EB)
Image                                  7ff8`49102000        0`10000000 ( 256.000 MB)

这里简单提一下,我发现有很多朋友搞不清楚这里的 16.000 EB 是什么意思,它其实是2的64次方,即程序的用户态空间的寻址范围。

从卦中的 MEM_COMMIT=7.544G 来看当前提交内存不小,接下来用 !eeheap -gc 观察下托管堆内存占用。


0:000> !eeheap -gc

========================================
Number of GC Heaps: 1
----------------------------------------
generation 0 starts at 7ff688f78f10
generation 1 starts at 7ff688484e70
generation 2 starts at 7ff8f7fff000
ephemeral segment allocation context: none
Small object heap
         segment            begin        allocated        committed allocated size         committed size        
    7ff63fffa000     7ff63fffb000     7ff64fe51d80     7ff64fe5d000 0xfe56d80 (266694016)  0xfe63000 (266743808) 
    7ff6772c8000     7ff6772c9000     7ff6872c2d38     7ff6872c8000 0xfff9d38 (268410168)  0x10000000 (268435456)
    7ff74bffe000     7ff74bfff000     7ff75bffdfc0     7ff75bffe000 0xfffefc0 (268431296)  0x10000000 (268435456)
    7ff773ffe000     7ff773fff000     7ff783ffdfc8     7ff783ffe000 0xfffefc8 (268431304)  0x10000000 (268435456)
    7ff849102000     7ff849103000     7ff859101fe8     7ff859102000 0xfffefe8 (268431336)  0x10000000 (268435456)
    7ff8f7ffe000     7ff8f7fff000     7ff907ffce88     7ff907ffe000 0xfffde88 (268426888)  0x10000000 (268435456)
    7ff6872ca000     7ff6872cb000     7ff68a768438     7ff68aa4b000 0x349d438 (55170104)   0x3781000 (58200064)  
Large object heap starts at 7ff907fff000
         segment            begin        allocated        committed allocated size         committed size        
    7ff733ff8000     7ff733ff9000     7ff73aedd058     7ff73aefe000 0x6ee4058 (116277336)  0x6f06000 (116416512) 
    7ff743ffc000     7ff743ffd000     7ff744358f10     7ff744379000 0x35bf10 (3522320)     0x37d000 (3657728)    
    7ff7a3ffe000     7ff7a3fff000     7ff7a9d63ee0     7ff7a9d84000 0x5d64ee0 (97930976)   0x5d86000 (98066432)  
    7ff7bbffe000     7ff7bbfff000     7ff7c3dc1090     7ff7c3de2000 0x7dc2090 (131866768)  0x7de4000 (132005888) 
    7ff907ffe000     7ff907fff000     7ff90f048b30     7ff90f069000 0x7049b30 (117742384)  0x706b000 (117878784) 
Pinned object heap starts at 7ff90ffff000
         segment            begin        allocated        committed allocated size         committed size        
    7ff90fffe000     7ff90ffff000     7ff9102d15b0     7ff9102d2000 0x2d25b0 (2958768)     0x2d4000 (2965504)    
------------------------------
GC Allocated Heap Size:    Size: 0x7f36bca0 (2134293664) bytes.
GC Committed Heap Size:    Size: 0x7f710000 (2138112000) bytes.

从卦中看当前提交内存也仅有 2.13G,这和 7.5G 相距甚远,说明这是最复杂的 非托管内存泄漏

2. 非托管泄露分析

作为一个.NET调试者,需要像医生一样尽自己最大可能救治病人,那接下来我们的研究方向在哪里呢?大家需要知道所有的内存占用的基本盘都在 虚拟地址 上,结果一搜索,发现有大概 4w+ 的 dll,一个程序怎么可能会有这么多动态链接库呢?截图如下:

记一次 .NET某报关系统 非托管泄露分析

既然找到了可疑之处那就继续挖吧,接下来就是要考虑这个dll是托管代码创建的还是非托管代码创建的,用排除法就好了,如果是托管代码创建的,那就肯定属于 Assembly 下的某一个module,可以查下加载堆看看。


0:000> !eeheap -loader
Loader Heap:
--------------------------------------
...
Module 00007ff95e265778: Size: 0x0 (0) bytes.
Module 00007ff95e2661e0: Size: 0x0 (0) bytes.
Module 00007ff95e266c48: Size: 0x0 (0) bytes.
Module 00007ff95e2676b0: Size: 0x0 (0) bytes.
Module 00007ff95e268118: Size: 0x0 (0) bytes.
Module 00007ff95e268b80: Size: 0x0 (0) bytes.
Module 00007ff95e2695e8: Size: 0x0 (0) bytes.
Total size:      Size: 0x0 (0) bytes.
--------------------------------------
Total LoaderHeap size:   Size: 0x4bf4b000 (1274327040) bytes total, 0x4da000 (5087232) bytes wasted.
=======================================

0:000> !dumpmodule 00007ff95e2695e8
Name: *75db8939-8b3a-4075-94ac-e9bb52acf9d1#40147-0.dll
Attributes:              PEFile IsInMemory IsFileLayout 
...
MetaData start address:  00007FF85BBCD330 (2068 bytes)

0:000> !DumpAssembly /d 00007ff80d71bba0
Parent Domain:      0000559009249080
Name:               Unknown
ClassLoader:        00007FF80D71BC00
  Module
  00007ff95e2695e8    *75db8939-8b3a-4075-94ac-e9bb52acf9d1#40147-0.dll

从卦中看虽然加载堆只有 1.27G,但它还有很多关联的内存,而且动态module高多4w+,接下来用 !dumpmodule -mt 观察内部是什么类型。


0:000> !dumpmodule -mt 00007ff95e2695e8
Name: *75db8939-8b3a-4075-94ac-e9bb52acf9d1#40147-0.dll
Attributes:              PEFile IsInMemory IsFileLayout 
...
Types defined in this module

              MT          TypeDef Name
------------------------------------------------------------------------------
00007ff95e269d80 0x02000004 Submission#0
00007ff95e269ec0 0x02000005 Submission#0+<>d__0

Types referenced in this module

              MT            TypeRef Name
------------------------------------------------------------------------------
00007ff92d6152a8 0x0200000c System.Object
00007ff92ead4d18 0x0200000d System.Threading.Tasks.Task`1
00007ff93133f298 0x0200000e System.Runtime.CompilerServices.IAsyncStateMachine
00007ff92d6cec90 0x0200000f System.Exception
00007ff93133f648 0x02000010 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1

0:000> !dumpmt -md 00007ff95e269ec0
...
MethodDesc Table
           Entry       MethodDesc    JIT Name
00007FF92D620030 00007ff92d615238    JIT System.Object.Finalize()
00007FF92D620038 00007ff92d615248 PreJIT System.Object.ToString()
00007FF92D620040 00007ff92d615258    JIT System.Object.Equals(System.Object)
00007FF92D620058 00007ff92d615298    JIT System.Object.GetHashCode()
00007FF95DFFB0E0 00007ff95e269e58    JIT Submission#0+<>d__0.MoveNext()
00007FF95DFF9B70 00007ff95e269e78   NONE Submission#0+<>d__0.SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)
00007FF95DFF9B60 00007ff95e269e48    JIT Submission#0+<>d__0..ctor()

从卦中看也只能看到一些 Submission 为前缀的类与之相关的状态机类,也看不出来是谁创建的,结果又入了困境。

3. 到底是谁作的孽

要想获取动态程序集的创建事件,有一个好办法就是用跨平台的 dotnet-trace,让它捕获程序集的加载事件即可,详情可参考:https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/collect-details ,然后让朋友跑 30min 看看,参考命令如下:


dotnet-trace collect -p 4108 --clrevents loader --duration 00:00:30:00

有了生成好的 dotnet_xxxx.nettrace 之后就可以用 perfview 观察了,打开 Event视图,搜索 AssemblyLoad 事件,截图如下:

记一次 .NET某报关系统 非托管泄露分析

通过 Time MSec 的748前缀来看,这1s种能生成几十个动态程序集,接下来右键选择 Open Any Stacks 观察是什么代码调用的,截图如下:

记一次 .NET某报关系统 非托管泄露分析

从 perfivew 的输出看,原来是 XXXCusDis 方法内部调用 Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync 生成了非常多的程序集。

最后就是把 CSharpScript.EvaluateAsync 告诉朋友,能不能给剔除掉做个排查?

三:总结

网上查了下 Microsoft.CodeAnalysis.CSharp.Scripting 可以用来生成C#脚本代码,大家在用的时候小心点吧。文章来源地址https://www.toymoban.com/news/detail-632821.html

记一次 .NET某报关系统 非托管泄露分析

到了这里,关于记一次 .NET某报关系统 非托管泄露分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 记一次 .NET某防伪验证系统 崩溃分析

    昨晚给训练营里面的一位朋友分析了一个程序崩溃的故障,因为看小伙子昨天在群里问了一天也没搞定,干脆自己亲自上阵吧,抓取的dump也是我极力推荐的用 procdump 注册 AEDebug 的方式,省去了很多沟通成本。 windbg有一个非常强大的点就是当你双击打开后,会自动帮你切换到

    2024年03月28日
    浏览(63)
  • 记一次 .NET 某企业内部系统 崩溃分析

    前些天有位朋友找到我,说他的程序跑着跑着就崩溃了,让我看下怎么回事,其实没怎么回事,抓它的 crash dump 就好,具体怎么抓也是被问到的一个高频问题,这里再补一下链接: [.NET程序崩溃了怎么抓 Dump ? 我总结了三种方案] https://www.cnblogs.com/huangxincheng/p/14811953.html ,采用

    2024年02月10日
    浏览(53)
  • 记一次 .NET某列控连锁系统 崩溃分析

    过年喝了不少酒,脑子不灵光了,停了将近一个月没写博客,今天就当新年开工写一篇吧。 去年年初有位朋友找到我,说他们的系统会偶发性崩溃,在网上也发了不少帖子求助,没找到自己满意的答案,让我看看有没有什么线索,看样子这是一个牛皮藓的问题,既然对方有了

    2024年02月21日
    浏览(55)
  • 记一次 .NET 某埋线管理系统 崩溃分析

    经常有朋友跟我反馈,说看你的文章就像看天书一样,有没有一些简单入手的dump 让我们先找找感觉,哈哈,今天就给大家带来一篇入门级的案例,这里的入门是从 WinDbg 的角度来阐述的,这个问题如果你通过 记日志,分析代码 的方式,可能真的无法解决,不信的话继续往下

    2024年02月11日
    浏览(54)
  • 记一次 .NET 某券商论坛系统 卡死分析

    前几个月有位朋友找到我,说他们的的web程序没有响应了,而且监控发现线程数特别高,内存也特别大,让我帮忙看一下怎么回事,现在回过头来几经波折,回味价值太浓了。 这个程序内存高,线程高,无响应,尼玛是一个复合态问题,那怎么入手呢?按经验推测,大概率是

    2024年02月05日
    浏览(64)
  • 记一次 .NET 某工控视觉系统 卡死分析

    前段时间有位朋友找到我,说他们的工业视觉软件僵死了,让我帮忙看下到底是什么情况,哈哈,其实卡死的问题相对好定位,无非就是看主线程栈嘛,然后就是具体问题具体分析,当然难度大小就看运气了。 前几天看一篇文章说现在的 .NET程序员 不需要学习 WinDbg ,理由就

    2024年02月12日
    浏览(48)
  • 记一次 .NET 某电力系统 内存暴涨分析

    前些天有位朋友找到我,说他生产上的程序有内存暴涨情况,让我帮忙看下怎么回事,最简单粗暴的方法就是让朋友在内存暴涨的时候抓一个dump下来,看一看大概就知道咋回事了。 这个问题说的再多也不为过,一定要看清楚这个程序是如何个性化发展的,可以使用 !address

    2024年02月08日
    浏览(47)
  • 记一次 .NET某道闸收费系统 内存溢出分析

    前些天有位朋友找到我,说他的程序几天内存就要爆一次,不知道咋回事,找不出原因,让我帮忙看一下,这种问题分析dump是最简单粗暴了,拿到dump后接下来就是一顿分析。 程序既然会爆,可能是虚拟地址受限,也可能是系统内存不足,可以用 !address -summary 观察下。 从卦

    2024年01月18日
    浏览(47)
  • 记一次 .NET 某药材管理系统 卡死分析

    前段时间有位朋友找到我,说他们在查询报表的时候发现程序的稳定性会受到影响,但服务器的内存,CPU都是正常的,让我帮忙看下怎么回事,问了下程序的稳定性指的是什么?指的是卡死,那既然是卡死,就抓一个卡死的dump吧。 不同的程序类型分析卡死的思路是不一样的

    2024年02月09日
    浏览(58)
  • 记一次 .NET某炉膛锅炉检测系统 崩溃分析

    上个月有个朋友在微信上找到我,说他们的软件在客户那边隔几天就要崩溃一次,一直都没有找到原因,让我帮忙看下怎么回事,确实工控类的软件环境复杂难搞,朋友手上有一个崩溃的dump,刚好丢给我来分析一下。 windbg 有一个厉害之处在于双击之后可以帮你自动定位到崩

    2024年04月17日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包