论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
只需一步,快速开始
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
ToB企服应用市场:ToB评测及商务社交产业平台
»
论坛
›
软件与程序人生
›
后端开发
›
.Net
›
记一次 .NET某账本软件 非托管泄露分析
记一次 .NET某账本软件 非托管泄露分析
汕尾海湾
金牌会员
|
2023-10-16 21:38:21
|
显示全部楼层
|
阅读模式
楼主
主题
930
|
帖子
930
|
积分
2790
一:背景
1. 讲故事
中秋国庆长假结束,哈哈,在老家拍了很多的短视频,有兴趣的可以上B站观看:
https://space.bilibili.com/409524162
,今天继续给大家分享各种奇奇怪怪的.NET生产事故,希望能帮助大家在未来的编程之路上少踩坑。
话不多说,这篇看一个.NET程序集泄露导致的CLR私有堆泄露的案例,这个泄露和 JsonConvert 有关,哈哈,相信你肯定比较惊讶!
二:WinDbg 分析
1. 到底是哪里的泄露
首先观察一下进程的提交内存的大小,即通过 !address -summary 观察。
0:000> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free 390 7dfa`63fa8000 ( 125.978 TB) 98.42%
<unknown> 13628 205`32974000 ( 2.020 TB) 99.92% 1.58%
Heap 8143 0`4042b000 ( 1.004 GB) 0.05% 0.00%
Stack 186 0`1f8e0000 ( 504.875 MB) 0.02% 0.00%
Image 1958 0`09775000 ( 151.457 MB) 0.01% 0.00%
Other 9 0`001d7000 ( 1.840 MB) 0.00% 0.00%
TEB 62 0`0007c000 ( 496.000 kB) 0.00% 0.00%
PEB 1 0`00001000 ( 4.000 kB) 0.00% 0.00%
--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_MAPPED 312 200`00a06000 ( 2.000 TB) 98.92% 1.56%
MEM_PRIVATE 21717 5`91ecd000 ( 22.280 GB) 1.08% 0.02%
MEM_IMAGE 1958 0`09775000 ( 151.457 MB) 0.01% 0.00%
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE 390 7dfa`63fa8000 ( 125.978 TB) 98.42%
MEM_RESERVE 4509 205`0fc14000 ( 2.020 TB) 99.89% 1.58%
MEM_COMMIT 19478 0`8c434000 ( 2.192 GB) 0.11% 0.00%
复制代码
当前的提交内存占用了 2.19G,进程堆占用 1G ,差不多占了一半,但不能说明就是非托管内存泄露,接下来继续观察下托管堆。
0:000> !eeheap -gc
Number of GC Heaps: 8
------------------------------
Heap 7 (000001C4971013A0)
generation 0 starts at 0x000001C817D201A0
generation 1 starts at 0x000001C817C878D8
generation 2 starts at 0x000001C817261000
ephemeral segment allocation context: none
segment begin allocated size
000001C817260000 000001C817261000 000001C819013F98 0x1db2f98(31141784)
Large object heap starts at 0x000001C907261000
segment begin allocated size
000001C907260000 000001C907261000 000001C907261018 0x18(24)
Pinned object heap starts at 0x000001C987261000
000001C987260000 000001C987261000 000001C9872ABA50 0x4aa50(305744)
Heap Size: Size: 0x1dfda00 (31447552) bytes.
------------------------------
GC Heap Size: Size: 0xba26488 (195191944) bytes.
复制代码
从卦中可以看到当前的托管堆占用仅 195M,这就更好的验证当前确实存在非托管内存泄露,由于非托管内存没有开启 ust,也没有 perfview 的etw文件,所以没有好的方式进一步挖掘,到这里可能就止步不前了。
2. 到底是哪里的泄露
在 C# 所处的 Windows 进程中,其实有很多的堆,比如:crt堆,ntheap堆,gc堆,clr私有堆,堆外(VirtualAlloc),调试没有标准答案,不断的假设,试探,摸着石头过河,言外之意就是这个堆没问题,不代表其他堆也没有问题,这样想思路就比较顺畅了,我们可以看看其他的堆,比如这里的 CLR私有堆,使用 !eeheap -loader 观察。
0:000> !eeheap -loader
Loader Heap:
--------------------------------------
...
Module 00007ff846e034c0: Size: 0x0 (0) bytes.
Module 00007ff846e03930: Size: 0x0 (0) bytes.
Module 00007ff846e04180: Size: 0x0 (0) bytes.
Module 00007ff846e047e0: Size: 0x0 (0) bytes.
Module 00007ff846e04e40: Size: 0x0 (0) bytes.
Total size: Size: 0x0 (0) bytes.
--------------------------------------
Total LoaderHeap size: Size: 0x47252000 (1193615360) bytes total, 0x1f68000 (32931840) bytes wasted.
=======================================
复制代码
从卦中可以看到有非常多的 module 迸射出来,估计有几万个,并且可以看到总的大小是 1.19G,到这里基本就搞清楚了,然来是 程序集泄露。
这里稍微补充一下,像这种问题早期可以使用 dotnet-counter 或者 Windows 的程序集指标 监控一下,或许你就能轻松找出原因,截图如下:
PS C:\Users\Administrator\Desktop> dotnet-counters monitor -n WebApplication2
复制代码
而且 dotnet-counter 还是跨平台的,非常实用,大家可以琢磨琢磨,接下来抽一个module 用命令 !dumpmodule -mt 00007ff846e034c0 观察下,内部到底有哪些类型。
0:000> !dumpmodule -mt 00007ff846e034c0
Name: Unknown Module
Attributes: Reflection IsDynamic IsInMemory
Assembly: 000001c9e193b9e0
BaseAddress: 0000000000000000
...
Types defined in this module
MT TypeDef Name
------------------------------------------------------------------------------
00007ff846e03db0 0x02000002
Types referenced in this module
MT TypeRef Name
------------------------------------------------------------------------------
00007ff820ff5748 0x02000002 xxx.xxx.Json.Converters.PolymorphismConverter`1
00007ff820e710f8 0x02000003 xxx.xxx.Models.IApiResult
0:000> !dumpmt -md 00007ff846e03db0
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
00007FF822F05FA8 00007ff823285b50 NONE xxx.Json.Converters.PolymorphismConverter`1
00007FF822EFD5E8 00007ff82323b1b8 NONE System.Text.Json.Serialization.JsonConverter`1
00007FF822EFD5F0 00007ff82323b1c8 NONE System.Text.Json.Serialization.JsonConverter`1
00007FF8414CB978 00007ff846e03d88 JIT IApiResultDynamicJsonConverter..ctor()
复制代码
仔细分析卦中信息,可以很明显的看到。
Json.Converters.PolymorphismConverter
看样子和牛顿有关系,并且还是一个自定义的 JsonConvert。
IApiResult 和 IApiResultDynamicJsonConverter
看样子是一个接口的返回协议类,需要在代码中重点关注。
有了这些信息,接下来就是重点关注代码中的 PolymorphismConverter 类,果然就找到了一处。
从类的定义来看,一般这种东西都是在 ConfigureServices 方法中做 初始化定义 的,按理说问题不大,那为什么会有问题呢?还得要查下它的引用,终于给找到了,截图如下:
这是一个低级错误哈,每次读取 ApiResult.Data 的时候都要 jsonSerializerOptions.AddPolymorphism(); 操作,也就每次都会创建程序集,终于真相大白。
三:总结
这种程序集泄露导致的生产事故不应该哈,反应了团队中多人协作的时候还是有待提高!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
本帖子中包含更多资源
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
回复
使用道具
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
汕尾海湾
金牌会员
这个人很懒什么都没写!
楼主热帖
RabbitMQ 进阶 -- 阿里云服务器部署Rab ...
Spark快速上手(3)Spark核心编程-RDD转 ...
在Ubuntu系统上安装StoneDB数据库 ...
基于FPGA的一维卷积神经网络CNN的实现 ...
一文了解袋鼠云在实时数据湖上的探索与 ...
Vue 全套教程(二),入门 Vue 必知必 ...
redis实现主从复制
用开源github,还是咱中国自己的代码托 ...
5分钟安装Kubernetes+带你轻松安装isti ...
windows安装mysql8.0.29(ZIP解压安装 ...
标签云
存储
挺好的
服务器
快速回复
返回顶部
返回列表