记一次 .NET 某低代码开发框架 内存暴涨分析

一:背景1. 讲故事

微信里有一位朋友找到我,说他们公司的程序存在内存暴涨问题,自己分析了下没有找到原因,让我看下怎么回事?由于大家都有dump分析基础,所以交流互通上还是很顺利的,接下来就是上dump分析啦。

二:内存暴涨分析1. 为什么会内存暴涨

先还是老套路,用 !address -summary观察下内存分布情况,输出如下:

0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal

Free 3637dfd`e87c7000 ( 125.992TB) 98.43%

Heap 650`2547f000 ( 596.496MB) 0.03% 0.00%

Image 18550`09d35000 ( 157.207MB) 0.01% 0.00%

Stack 930`02c00000 ( 44.000MB) 0.00% 0.00%

Other 90`001de000 ( 1.867MB) 0.00% 0.00%

TEB 310`0003e000( 248.000kB) 0.00% 0.00%

PEB 10`00001000( 4.000kB) 0.00% 0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal

MEM_FREE 3637dfd`e87c7000 ( 125.992TB) 98.43%

从卦中可以看到,总计 3.6G的总提交内存,看样子都落到了 Unk区域,最好是托管层吃掉了,否则就麻烦了,接下来使用 !dumpheap -stat观察,输出如下:

0:000> !dumpheap -stat

Statistics:

MT Count TotalSize Class Name

...

7ffc6e0a2888 2536,870,960System.WeakReference []

7ffc6e0a2260 60,873,9781,460,975,472System.WeakReference

Total 63,333,893objects, 2,494,520,292bytes

从卦中可以看到程序中有 6087w个弱引用,接下来使用 !dumpheap -mt 7ffc6e0a2260观察下列表详情,然后用 !gcroot观察其引用根,参考如下:

0:000> !dumpheap -mt 7ffc6e0a2260

Address MT Size

...

017a405f1020 7ffc6e0a2260 24

Caching GC roots, thismay take a while.

Subsequent runs of thiscommand will be faster.

等了20多分钟都没有出来结果,可能 6kw 的根纵横交错让windbg不堪重负,没有就没撤了,使用内存搜索法寻找上级所属对象。这里就选择 017a405f1020对象来开刀。

0:000> !dumpobj /d 17a405f1020

Name: System.WeakReference`1[[Microsoft.Extensions.DependencyInjection.ServiceProvider, Microsoft.Extensions.DependencyInjection]][]

MethodTable: 00007ffc6e0a2888

EEClass: 00007ffc6dbeb4f8

Tracked Type: false

Fields:

None

0:000> s-q 0 L?0xffffffffffffffff 17a405f1020

00000179`c95861d0 0000017a`405f1020 03a0dcfa`03a0dcfa

0:000> !lno 0000017a`405f1020

Before: 017a405f1000 32 (0x20) Free

Current: 017a405f1020 24 (0x18) System.WeakReference []

Error Detected: Object 17a405f1020 has a bad member at offset 12054c00: ??? [verify heap]

Could not find objectafter 17a405f1020

Heap local consistency not confirmed.

0:000> !lno 00000179`c95861d0

Before: 0179c95861c8 32 (0x20) System.Collections.Generic.List >

Next: 0179c95861e8 24 (0x18) System.WeakReference []

Heap local consistency confirmed.

0:000> !dumpobj /d 179c95861c8

Name: System.Collections.Generic.List`1[[System.WeakReference`1[[Microsoft.Extensions.DependencyInjection.ServiceProvider, Microsoft.Extensions.DependencyInjection]], System.Private.CoreLib]]

MethodTable: 00007ffc6e0a2340

EEClass: 00007ffc6dce0000

Tracked Type: false

Size: 32(0x20) bytes

File: D:\xxx\A_api\System.Private.CoreLib.dll

Fields:

MT Field Offset Type VT Attr Value Name

00007ffc6de328f0 40020a2 8 System.__Canon[] 0 staticdynamicstatics NYI s_emptyArray

0:000> s-q 0 L?0xffffffffffffffff 179c95861c8

00000179`c77571d8 00000179`c95861c8 00000000`00000000

0:000> !lno 00000179`c77571d8

Failed to find the segment of the managed heap wherethe object179c77571d8 resides

0:000> !lno 00000179`c95861b8

Next: 0179c95861c8 32 (0x20) System.Collections.Generic.List >

Heap local consistency confirmed.

根据卦中的图和输出,终于找到了原来是 DependencyInjectionEventSource._providers承担了所有,接下来的关注点就来到了 DependencyInjectionEventSource。

2. xxxEventSource 是什么

从名字上看和 ETW 事件有关,接下来用 !eeversion

0:000> !eeversion

6.0.3624.51421free

Workstation mode

SOS Version: 9.0.13.2701retail build

修改后的代码如下,果然加了很多的业务逻辑来处理。

[NonEvent]

publicvoidServiceProviderBuilt(ServiceProvider provider)

{

lock(_providers)

{

intprovidersCount = _providers.Count;

if(providersCount > 0&&

(_survivingProvidersCount isintspc ? (uint)providersCount >= 2* (uint)spc : providersCount == _providers.Capacity))

{

_providers.RemoveAll(staticp => !p.TryGetTarget(out_));

_survivingProvidersCount = _providers.Count;

}

_providers.Add(newWeakReference (provider));

}

WriteServiceProviderBuilt(provider);

}

从官方描述来看,就是有人创建了 scope,但后续没有调用 dispose 方法来及时释放,导致框架中的 WeakReference 引用滞留,引发内存暴涨,可以说两者都有责任吧。

解决办法很简单,两种方式:

检查代码里写 BuildServiceProvider 的地方没有即时的 Dispose。

升级到 .NET10 ,这是最简单粗暴的方法。

检查代码里写 BuildServiceProvider 的地方没有即时的 Dispose。

升级到 .NET10 ,这是最简单粗暴的方法。

把结论告诉朋友后,朋友终于在2天后给我反馈了好消息,好心情溢于言表!

三:总结

dump之旅是一个修理工不断自我修炼的过程,必须学会在绝望中寻找希望的能力。

原创文章,作者:马超,如若转载,请注明出处:http://m.gaochengzhenxuan.com/news/10448.html

(0)
马超马超
上一篇 2026-03-12
下一篇 2026-03-12

相关推荐

  • 2026江苏学习困难专业干预机构推荐指南

    学习困难,并非简单的“不努力”或“贪玩”。据《中国注意缺陷多动障碍防治指南》数据,我国儿童青少年ADHD(多动症)患病率约为6.4%,这意味着一个40人的班级中,可能就有2-3名孩子正因此饱受学业困扰。由此引发的注意力涣散、作业拖拉、成绩下滑等问题,成为无数家

    2026-04-03
    604
  • 深粮控股董事会会议:2026年内部审计与投资计划揭晓

    2026年3月31日,深粮控股(SZ000019)在深圳市福田区召开了第十一届董事会第二十三次会议,吸引了市场的广泛关注。此次会议的主要议题是审议2026年内部审计及投资项目后评价工作计划,相关文件的通过将对公司的未来发展起到至关重要的作用。随着市场环境的不断

    2026-04-03
    597
  • 泉州妇幼保健院内窥镜采购:生物反馈治疗仪为何废标?

    近日,泉州市妇幼保健院(泉州市儿童医院)发布了医用内窥镜一批(三次)的采购结果公告,其中采购包1,即生物反馈治疗仪的采购结果为“废标”。这究竟是怎么回事?让我们一起解读这份公告,看看隐藏在背后的原因。什么是生物反馈治疗仪?在深入探讨废标原因之前,我们先来了解一

    2026-04-03
    881