记一次 .NET 某制造业 MES 系统崩溃分析

一:背景

1.讲故事

前段时间有位朋友微信找到我,说他的程序偶尔会出现内存溢出崩溃,让我帮忙看下是怎么回事,咨询了下程序是 x86 部署,听到这个词其实心里已经有了数,不管怎么样还是用 windbg 分析一下。

二:WinDbg 分析

1. x86 程序意味着什么

x86 程序意味着程序默认只能吃到 2G 的内存,或者说只能用 2G 的虚拟地址,这种类型的程序很容易出现 虚拟地址紧张 造成崩溃,那怎么去验证程序只能吃 2G 内存呢?通常有两种做法:

1) 使用 !dh 查看 PE 头

可以用 lm 找到 exe 模块,然后使用 !dh Device_xxx 观察 PE 头,代码如下:

0:000> lm
start    end        module name
00360000 0099a000   xxxDevice C (service symbols: CLR Symbols without PDB)        
157f0000 15abf000   QQPinyin   (export symbols)       QQPinyin.ime
....0:000> !dh xxxDeviceFile Type: EXECUTABLE IMAGE
FILE HEADER VALUES14C machine (i386)3 number of sections
6305F7E8 time date stamp Wed Aug 24 18:05:28 20220 file pointer to symbol table0 number of symbolsE0 size of optional header102 characteristicsExecutable32 bit word machine
...

最后一行的 32 bit word machine 表示是纯纯 x86,但在我的分析旅行中,这种也不是特别准,曾经遇到程序开启了 大地址,最后也只能吃 2G 内存,这就很奇葩了,所以更准的方式就是用 !address 看内存段。

  1. 使用 !address 查看内存段

这种做法万无一失,输出如下:

0:000> !addressBaseAddr EndAddr+1 RgnSize     Type       State                 Protect             Usage
-----------------------------------------------------------------------------------------------
+        0   360000   360000             MEM_FREE    PAGE_NOACCESS                      Free   
...
+ 7ffe1000 7ffec000     b000             MEM_FREE    PAGE_NOACCESS                      Free       
+ 7ffec000 7ffed000     1000 MEM_PRIVATE MEM_COMMIT  PAGE_READONLY                      <unknown>  [HalT............]
+ 7ffed000 7fff0000     3000             MEM_FREE    PAGE_NOACCESS                      Free       0:000> ? 7fff0000 /0x100000
Evaluate expression: 2047 = 000007ff

卦中最后一个内存段地址为 7fff0000,也就是 2G 的意思,所以最好的办法就是让朋友开启大地址解决,那大地址怎么开,用 anycpu 编译即可,但有很多朋友反馈用 anycpu 的话,很多 C++ 的链接库会报错,所以更好的做法是参考这篇:https://www.cnblogs.com/huangxincheng/p/15671957.html

到这里,貌似就可以结案了。。。

2. 真的要让 2G 地址背锅吗

开启大地址可以让程序吃到更多的内存,这个不假,但 放之四海而皆准 也不见得,言外之意还得分析下内存是怎么被吃掉的?如果是程序本身的问题,不断的侵蚀内存,再多的内存也不够用,对吧。

作为一个负责任的 调试博主,还是不要简单忽悠过去,接下来用 !address -summary 观察下内存布局。

0:000> !address -summary--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
<unknown>                              1221          551c4000 (   1.330 GB)  79.82%   66.49%
Free                                    258          155dd000 ( 341.863 MB)           16.69%
Image                                   916           b1c6000 ( 177.773 MB)  10.42%    8.68%
Stack                                   303           62c0000 (  98.750 MB)   5.79%    4.82%
Heap                                    129           426f000 (  66.434 MB)   3.89%    3.24%
TEB                                     101             f7000 ( 988.000 kB)   0.06%    0.05%
Other                                    12             60000 ( 384.000 kB)   0.02%    0.02%
PEB                                       1              3000 (  12.000 kB)   0.00%    0.00%--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE                            1437          561fd000 (   1.346 GB)  80.77%   67.29%
MEM_IMAGE                              1180           d23f000 ( 210.246 MB)  12.32%   10.27%
MEM_MAPPED                               66           75d7000 ( 117.840 MB)   6.91%    5.75%--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_COMMIT                             2113          5ce03000 (   1.451 GB)  87.10%   72.56%
MEM_FREE                                258          155dd000 ( 341.863 MB)           16.69%
MEM_RESERVE                             570           dc10000 ( 220.062 MB)  12.90%   10.75%...

从输出看,当前提交内存为:MEM_COMMIT = 1.45G,以我的经验来说, 1.2G 是一个警戒线,一旦过了,程序崩溃的概率会几何倍提升。

<unknown>=1.33G 来看,内存可能都被 GC堆 或者 VirtualAlloc 吃掉了,为了进一步验证,需要看下托管堆。

0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x5ff45f5c
generation 1 starts at 0x5fee1000
generation 2 starts at 0x02d81000
ephemeral segment allocation context: nonesegment     begin  allocated      size
02d80000  02d81000  03d7fc50  0xffec50(16772176)
098c0000  098c1000  0a8bfc10  0xffec10(16772112)
0abf0000  0abf1000  0bbefc90  0xffec90(16772240)
0e0a0000  0e0a1000  0f09ff40  0xffef40(16772928)
13640000  13641000  1463fee8  0xffeee8(16772840)
17e40000  17e41000  18e3fff8  0xffeff8(16773112)
...
5fee0000  5fee1000  603b86e4  0x4d76e4(5076708)
Large object heap starts at 0x03d81000segment     begin  allocated      size
03d80000  03d81000  04cd0f70  0xf4ff70(16056176)
3e8f0000  3e8f1000  3f88bd80  0xf9ad80(16362880)
3f8f0000  3f8f1000  4075eeb0  0xe6deb0(15130288)
Total Size:              Size: 0x44c33258 (1153643096) bytes.
------------------------------
GC Heap Size:    Size: 0x44c33258 (1153643096) bytes.

从卦中的 GC Heap Size= 1.15G 来看,原来都是被 GCHeap 给弄没了,它吃掉了这么多内存是正常还是异常现象呢?这个还是取决于程序的业务逻辑,比如人家有一个小缓存什么的。

3. 真的要让程序背锅吗

既然分析到这里,含着泪也得分析下去,可以使用 !dumpheap -stat 看下托管堆使用。

0:000> !dumpheap -stat
Statistics:MT    Count    TotalSize Class Name
71430958  1420366     66249092 System.Int32[]
...
174f4300  6852848    109645568 xxx.Mes.xxxPlatInfo
174f4194  6852848    164468352 xxx.Mes.Platxxx
7142eb40  6923571    210298518 System.String
00dbb9a0  1788917    434963034      Free

如果你有足够的分析经验,一看就能看出问题,比如 xxx.Mes.xxxPlatInfoxxx.Mes.Platxxx 对象高达 685w ,对象之间的排列布局很容易造成大量 Free 块,也叫做堆碎片化,真的很难看,类似下面这样。

0:000> !dumpheap 5cee1000  5dede324Address       MT     Size...
5cee17c8 00dbb9a0       38 Free
5cee17f0 174f4194       24     
5cee1808 7142eb40       30     
5cee1828 174f4300       16     
5cee1838 00dbb9a0       10 Free
5cee1844 174f4194       24     
5cee185c 7142eb40       30     
5cee187c 174f4300       16     
5cee188c 00dbb9a0       46 Free
5cee18bc 174f4194       24     
5cee18d4 7142eb40       30     
5cee18f4 174f4300       16     
5cee1904 00dbb9a0       10 Free
5cee1910 174f4194       24     
5cee1928 7142eb40       30     
5cee1948 174f4300       16     
5cee1958 00dbb9a0       50 Free
5cee198c 174f4194       24     
5cee19a4 7142eb40       30     
5cee19c4 174f4300       16     
5cee19d4 00dbb9a0      130 Free
...

接下来在 xxx.Mes.xxxPlatInfo 中抽一个对象观察它的引用根,为什么没有被 GC 回收。

a25792e38a044b31a0e1ece6acdb4ac5.png

从图中可以看到,它被 xxxPlatInfo 类下的 ConcurrentDictionary 中的 List 持有,我翻看了几个,Size 都比较大,比如下面输出:

0:000> !do 0ea19634
Name:        System.Collections.Generic.List`1[[xxx]]
MethodTable: 174f4350
EEClass:     71006b4c
Size:        24(0x18) bytes
Fields:MT    Field   Offset                 Type VT     Attr    Value Name
7143e0fc  400188f        4     System.__Canon[]  0 instance 04bd0f60 _items
71430994  4001890        c         System.Int32  1 instance   200367 _size
71430994  4001891       10         System.Int32  1 instance   200367 _version
7142eee0  4001892        8        System.Object  0 instance 00000000 _syncRoot
7143e0fc  4001893        4     System.__Canon[]  0   static  <no information>0:000> !DumpObj /d 0e6b9888
Name:       System.Collections.Generic.List`1[[xxx]]
MethodTable: 174f4350
EEClass:     71006b4c
Size:        24(0x18) bytes
Fields:MT    Field   Offset                 Type VT     Attr    Value Name
7143e0fc  400188f        4     System.__Canon[]  0 instance 3f78bd70 _items
71430994  4001890        c         System.Int32  1 instance   171806 _size
71430994  4001891       10         System.Int32  1 instance   171806 _version
7142eee0  4001892        8        System.Object  0 instance 00000000 _syncRoot
7143e0fc  4001893        4     System.__Canon[]  0   static  <no information>

将这些信息反馈给朋友后,朋友说 List 这么多是有问题的,排查之后是 List 在多线程情况下有问题,修正之后问题得到解决。

三:总结

这次事故主要是由于朋友在处理线程安全集合 ConcurrentDictionary<xxx, List<xxx>> 的过程中,对其中的 List<xxx> 没有合理的线程安全处理,导致数据的异常暴增,最终把紧张的 2G 虚拟地址用尽。

教训就是:key 线程安全了, value 也要记的安全哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/281379.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

首次公开!单日600PB的计算力--阿里巴巴EB级大数据平台的进击

摘要&#xff1a; 每年的双11之前&#xff0c;也是MaxCompute各种乾坤大挪移落定的时候&#xff0c;因为双11就是各种大折腾项目的自然deadline。在今年双11之前&#xff0c;一路向北迁移和在离线混部项目&#xff0c;将杭州集群除蚂蚁外整体迁移到张北&#xff0c;涉及了绝大部…

软件测试金字塔

软件测试金字塔 在敏捷方法中&#xff0c;持续集成是其基石&#xff0c;持续集成的核心是自动化测试。下面这篇关于测试金字塔的文章&#xff0c;来自大师Martin Fowler。 测试金字塔的概念来自Mike Cohn&#xff0c;在他的书Succeeding With Agile中有详细描述&#xff1a;测试…

使用pm2守护你的.NET Core应用程序

简介PM2是常用的node进程管理工具&#xff0c;它可以提供node.js应用管理&#xff0c;如自动重载、性能监控、负载均衡等。同类工具有Supervisor、Forever等。pm2是一个进程管理工具,可以用它来管理你的node进程&#xff0c;并查看node进程的状态&#xff0c;当然也支持性能监控…

程序员搞笑段子

转载于:https://www.cnblogs.com/Zhusi/p/10083474.html

CA周记 - 在 Azure ML 上用 .NET 跑机器学习

.NET 是一个跨平台&#xff0c;全场景应用的开源技术。你有在用 .NET 做机器学习/深度学习的应用吗&#xff1f;如果从框架角度&#xff0c;ML.NET / Tensorflow.NET / 不断在进步的 TorchSharp 通过几年的发展已经开始稳定&#xff0c;但如果在一些大型项目上&#xff0c;特别…

iOS10 优化APP首次安装网络权限提示方案

我刚经历了一场末日&#xff08;停电&#xff09;&#xff0c;特别是在你想写文档的时候。。。 言归正传&#xff0c;今天的问题是解决iOS10系统下首次按钮APP弹出的网络权限提示所带来了问题以及优化。 起因 查了相关文章知道由于大陆工信部出台的新规指出&#xff0c;应用在未…

su命令

从一个用户切换到另一个用户&#xff1a;su - ceshi(ceshi是用户名) 查看当前用户&#xff1a;whoami 在不切换用户的情况执行另一个用户的命令&#xff1a;例&#xff1a;su - -c "touch /tmp/111.txt" admin 若用户没有加目录需要添加家目录&#xff0c;并更改所有…

PS批量替换内容

在制作图片物料的时候&#xff0c;有时会碰到需要制作大量内容格式一致&#xff0c;但部分文字或图片不同的图片&#xff0c;这里我们使用PS的变量功能 物料准备&#xff1a;准备好需要替换的图片和文字&#xff0c;使用excel制作出需要替换的内容&#xff0c;第一行name和pic…

在 .NET 中执行 JavaScript 代码

你好&#xff0c;这里是 Dotnet 工具箱&#xff0c;定期分享 Dotnet 有趣&#xff0c;实用的工具和组件&#xff0c;希望对您有用&#xff01;Jint 简介如果您想在您的 .NET 程序中使用 Javascript&#xff0c;那么我推荐您使用 Jint。Jint 是适用于 .NET 的 开源 Javascript 解…

jmeter多用户登录跨线程组操作传值

项目需求&#xff1a; 需要登录两个用户A、B&#xff0c;用户A操作完后会通知B&#xff0c;然后B再操作&#xff0c;B操作完结束或者再通知A。 实现思路&#xff1a; 1. 设置两个线程组Ⅰ、Ⅱ&#xff0c;组Ⅰ添加cookie管理器&#xff0c;里面添加用户A的操作&#xff1b;组Ⅱ…

手动修改美化7zip图标 - 附替换文件

手动修改7zip图标理论知识制作ico图标文件替换资源重建图标缓存效果图替换文件7-Zip的自带图标是公认的丑&#xff0c;所以网上有很多7-Zip的图标美化工具&#xff0c;用的最多的就是 7-Zip Theme Manager&#xff0c;自带很多主题&#xff0c;但是最近我再使用7zTM修改图标是却…

.NET性能优化-复用StringBuilder

在之前的文章中&#xff0c;我们介绍了 dotnet 在字符串拼接时可以使用的一些性能优化技巧。比如&#xff1a;为StringBuilder设置 Buffer 初始大小使用ValueStringBuilder等等 不过这些都多多少少有一些局限性&#xff0c;比如StringBuilder还是会存在new StringBuilder()这样…

如何使用vue组件搭建网页并打包发布

vue组件化项目搭建及编译打包发布引言开发环境开发环境介绍开发环境安装使用模板创建项目编译及打包发布引言 最近开始学习Vue&#xff0c;Vue 是一个前端框架&#xff0c;特点是数据绑定和组件化。网上很多教程&#xff0c;数据绑定和组件学习起来也都不困难&#xff0c;但是…

Facebook揭秘其应用测试平台,并开源核心技术

本周&#xff0c;Facebook让一群记者参观了它位于俄勒冈州普赖恩维尔市的数据中心&#xff0c;在这个过程中也展示了他们的一个移动应用测试实验室&#xff0c;用来测试Facebook app、 Messenger 和Instagram。 目前 这个实验室总共含有60个机架&#xff0c;每个机架上放置32台…

责任链模式 职责链模式 Chain of Responsibility Pattern 行为型 设计模式(十七)

责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;职责链模式意图 使多个对象都有机会处理请求&#xff0c;从而避免请求的发送者和接受者之间的耦合关系将这些对象连接成一条链&#xff0c;并沿着这条链传递请求&#xff0c;直到有一个对象处理它为止。责任…

YOLOv7 在 ML.NET 中使用 ONNX 检测对象

本文介绍如何在 ML.NET 中使用 YOLOv7 的 ONNX 模型来检测图像中的对象。什么是 YOLOYOLO&#xff08;You Only Look Once&#xff09;是一种先进的实时目标检测系统。它是一个在COCO数据集上预训练的物体检测架构和模型系列&#xff0c;其版本也是在不断优化更新。2022年7月&a…

NppFTP小插件的使用

大家在Linux系统中配置运行环境时&#xff0c;一定会遇到相关配置文件的修改&#xff0c;虽说在Linux系统中可以使用vi,vim的命令进行文本编辑&#xff0c;但是操作起来还是没有在Windows系统中用的爽&#xff0c;特别是操作大文本量的文件时。Notepad里提供了一个小插件&#…

『实战』使用Excel催化剂二维码功能批量生成带不同图案二维码。

熟悉Excel催化剂的老读者都知道&#xff0c;每逢图书大促&#xff0c;笔者都会向出版社申请优惠码优惠券来惠及广大粉丝。当然公众号上帮出版社推荐图书&#xff0c;会有佣金回报&#xff0c;这也是笔者唯一能接受的推广方式。公众号自创立以来&#xff0c;没有接过软文&#x…

JavaScriptSerializer类 对象序列化为JSON,JSON反序列化为对象

JavaScriptSerializer 类由异步通信层内部使用&#xff0c;用于序列化和反序列化在浏览器和 Web 服务器之间传递的数据。说白了就是能够直接将一个C#对象传送到前台页面成为javascript对象。要添加System.Web.Extensions.dll的引用。该类位于System.Web.Script.Serialization命…

使用vue组件搭建网页应用

使用vue组件搭建网页应用搭建开发环境开发组件搭建开发环境 搭建一个 vue 项目最快的方式就是使用 vue cli 脚手架进行初始化&#xff0c;包含了所有完整的依赖及开发配置。 首先全局安装 vue cli&#xff0c;打开 cmd 命令提示符 或者 power shell&#xff0c;输入以下命令&…