记一次 .NET 医院CIS系统 内存溢出分析

一:背景

1. 讲故事

前几天有位朋友加wx求助说他的程序最近总是出现内存溢出,很崩溃,如下图:

和这位朋友聊下来,发现他也是搞医疗的,哈哈,.NET 在医疗方面还是很有市场的????????????,不过对于内存方面出的问题,我得先祈祷一下千万不要是非托管。。。

废话不多说,上 windbg,看能不能先救个急。

二:windbg 分析

1. 找出异常对象

如果内存溢出了,大家应该知道 C# 会抛一个 OutOfMemoryException 异常,而且还会附加到那个执行线程上,所以先用 !t 命令调出当前的所有托管线程。


0:000> !t
ThreadCount:      17
UnstartedThread:  0
BackgroundThread: 12
PendingThread:    0
DeadThread:       4
Hosted Runtime:   noLock  ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception0    1 16b0 007da908     26020 Preemptive  64EDD188:00000000 00823830 1     STA System.OutOfMemoryException 57b53d902    2  af8 007e9dc8     2b220 Preemptive  00000000:00000000 007d4838 0     MTA (Finalizer) 3    3 1d94 0081af28     21220 Preemptive  00000000:00000000 007d4838 0     Ukn 5    6 246c 0772b960   102a220 Preemptive  00000000:00000000 007d4838 0     MTA (Threadpool Worker) 8   47 277c 2eebf038   8029220 Preemptive  00000000:00000000 007d4838 0     MTA (Threadpool Completion Port) 
XXXX   41    0 2eebf580   1039820 Preemptive  00000000:00000000 007d4838 0     Ukn (Threadpool Worker)

可以清楚的看到,0号 线程果然带了一个 System.OutOfMemoryException,接下来用 !pe 查查这个异常的调用栈信息。


0:000> !pe 57b53d90
Exception object: 57b53d90
Exception type:   System.OutOfMemoryException
Message:          没有足够的内存继续执行程序。
InnerException:   <none>
StackTrace (generated):SP       IP       Function00482C80 6450BD46 mscorlib_ni!System.Runtime.InteropServices.Marshal.AllocHGlobal(IntPtr)+0xc2fdf600482CB0 198DCEF2 UNKNOWN!FastReport.Export.TTF.TrueTypeCollection..ctor(System.Drawing.Font)+0xe200482D00 198DCC0F UNKNOWN!FastReport.Export.TTF.ExportTTFFont.GetFontData()+0x4700482D58 198DAD54 UNKNOWN!FastReport.Export.Pdf.PDFExport.WriteFont(FastReport.Export.TTF.ExportTTFFont)+0xa400483A7C 198D9CD5 UNKNOWN!FastReport.Export.Pdf.PDFExport.AddPDFFooter()+0x8d00483C38 198D9B53 UNKNOWN!FastReport.Export.Pdf.PDFExport.Finish()+0x2300483C80 19938119 UNKNOWN!FastReport.Export.ExportBase.Export(FastReport.Report, System.IO.Stream)+0x22900483CD8 19937A9D UNKNOWN!FastReport.Export.ExportBase.Export(FastReport.Report, System.String)+0x4d00483D08 19937A3D UNKNOWN!FastReport.Report.Export(FastReport.Export.ExportBase, System.String)+0xd00483D10 15D9FA39 UNKNOWN!xxxx.xxx.FormPrint.PrintPdf(Boolean, System.String, xxxx.DAL.xxx.DataObject.IPatinfoBase, Boolean, System.String)+0x35900483DF0 137B265A UNKNOWN!xxxx.UI.xxx.PrintOrdert2PDF.Handle(System.Object[])+0x3ca00483EB4 1178B36C xxx_PrintOrder2Pdf!xxxx.xxx.PrintOrder2Pdf.Form1.timer1_Tick(System.Object, System.EventArgs)+0xca40048414C 117884DD UNKNOWN!System.Windows.Forms.Timer.OnTick(System.EventArgs)+0x1500484154 117883A0 UNKNOWN!System.Windows.Forms.Timer+TimerNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x3800484160 07C939B7 UNKNOWN!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0x5f

从上面的调用栈可以看出,貌似程序是在做一个 pdf 打印,最后在 Marshal.AllocHGlobal 上抛了异常,熟悉这个方法的朋友应该知道,它就是用来分配 非托管内存 的。。。情况貌似有点不妙。????????????

接下来用 ILSpy 查一下 AllocHGlobal 方法的源码,看看有什么可挖掘的地方。

从图中源码逻辑可以看出,一旦非托管内存分配失败,托管层上手工抛出 OutOfMemoryException 异常,我去,这难道是非托管内存溢出啦???

2. 真的是非托管溢出了吗?

要鉴别是否为非托管堆出的问题,还是用那个老办法,看看 MEM_COMMIT Size ≈ GC Heap Size  即可。

  • !address -summary 查看进程的内存使用量


0:000> !address -summary--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
<unknown>                             16334          460bb000 (   1.094 GB)  78.00%   54.72%
Free                                  11177          26319000 ( 611.098 MB)           29.84%
Image                                   831           e48e000 ( 228.555 MB)  15.91%   11.16%
Heap                                    184           4547000 (  69.277 MB)   4.82%    3.38%
Stack                                    61           11c0000 (  17.750 MB)   1.24%    0.87%
Other                                    10             60000 ( 384.000 kB)   0.03%    0.02%
TEB                                      20             24000 ( 144.000 kB)   0.01%    0.01%
PEB                                       1              3000 (  12.000 kB)   0.00%    0.00%--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_COMMIT                            16213          521bd000 (   1.283 GB)  91.43%   64.15%
MEM_FREE                              11177          26319000 ( 611.098 MB)           29.84%
MEM_RESERVE                            1228           7b1a000 ( 123.102 MB)   8.57%    6.01%

从上面的 MEM_COMMIT 指标可以看出内存使用量为 1.28 G

  • !gcheap -gc 看看托管堆的大小


0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x64c534f8
generation 1 starts at 0x64bccb84
generation 2 starts at 0x02531000
ephemeral segment allocation context: noneGC Heap Size:    Size: 0x195be7b0 (425453488) bytes.

从最后一行可以看出托管堆占用了 425453488/1024/1024 = 405M

也就是说大概 800M 不知道哪里去了,看似有点吓人,其实算算也还可以,这里我稍微补充一下,看下面的公式:

MEM_COMMIT (1.28G) = Image (228M) + Heap (69M) + Stack (18M) + GCHeap(450M) + GCLoader (153M) + else = 918M

从上面列出来的信息可以看出,最后累积出的 918M 和 内存使用量 1.28G 差不了多少,有些朋友可能要问, 这个 GCLoader 怎么算出来的,很简单,它是 CLR 的加载堆,使用 !eeheap -loader 即可。


0:000> !eeheap -loader
--------------------------------------
Total LoaderHeap size:   Size: 0x995a000 (160800768) bytes total, 0x13e000 (1302528) bytes wasted.
=======================================

到这里,我陷入了僵局????????????,才 1.28G 的内存占用,怎么就会把程序给弄溢出了?既然内存上看不出问题,那就从线程上入手吧,看看他们都在做什么?

3. 查看每个线程都在做什么?

要想看线程,可以用 ~*e !clrstack 调出所有线程的托管栈,突然我发现主线程有点奇怪,调用栈特别深,不信我截图跟你看。

从图中可以看到,xxx.xxx.PrintOrder2Pdf.Form1.timer1_Tick 高达 133 个,这说明 Form 窗体上有一个 timer 没有控制好,出现重复执行的情况了,不管怎么说,这个地方肯定有问题,接下来要做的就是把这个 timer1_Tick 源码导出来看看怎么写的,还是用那个 !name2ee + !savemodule 老命令导出,代码简化如下。


private void timer1_Tick(object sender, EventArgs e)
{if (!IsContinue){PrintMsg("等待上一扫描执行完毕");IsContinue = true;return;}IsContinue = false;GetPatList();if (PatList == null || PatList.Rows.Count == 0){timer1.Interval = 600000;PrintMsg("xxxx");IsContinue = true;return;}for (int i = 0; i < PatList.Rows.Count; i++){xxx}IsContinue=true;
}

从代码中可以看出,这个方法用了很多的 IsContinue 来踢掉重复请求,但最终还是出了bug,导致无限量递归,跟朋友沟通后建议用 Stop()Start() 来处理,参考如下代码:

private void button1_Click(object sender, EventArgs e){timer1.Interval = 2000;timer1.Tick += Timer1_Tick;timer1.Start();}private void Timer1_Tick(object sender, EventArgs e){timer1.Stop();MessageBox.Show("hello");timer1.Start();}

起码这种 停止启动 的方式肯定能规避timer的重复执行,先把这个改了再说,给医院那边先部署上,再观后效。。。

三:总结

朋友在五一节后,也就是前天给医院部署上了,昨天反馈没有再出现问题,截一张图证明一下????????????。

大家应该也看的出来,其实我心里是没底的。。。后续和朋友再沟通,发现了三点信息:

  • 医生的电脑配置为 8G or 12G

  • 有时候为了一些便利,医生会开双进程

  • 还有更多其他模块的内存溢出案例

看了下程序是采用插件式编程,而且还用了 DevExpress + FastReport 这些重量级的组件,再搭配上医生开的双进程让电脑余下的贫瘠内存更加吃紧,可能这才是程序在  1.2G 就分配不到非托管内存的深层原因,现场情况应该更复杂,只能先到这里了。

建议措施如下,很简单。

  • 增加电脑的配置,up 到 16G 最好了,毕竟甲方都不差钱 ????????????

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

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

相关文章

丘成桐:完全不懂数学,才会有“数学无用”的说法

全世界有3.14 % 的人已经关注了数据与算法之美▲中国科学院院长白春礼&#xff08;右&#xff09;与数学家丘成桐&#xff08;左&#xff09;为晨兴数学金奖获得者李思&#xff08;中&#xff09;颁奖培养第一流的学生&#xff0c;首先要有对于学问的兴趣&#xff0c;而非奔着考…

动手实现一个适用于.NET Core 的诊断工具

前言大家可能对诊断工具并不陌生&#xff0c;从大名鼎鼎的 dotTrace&#xff0c;到 .NET CLI 推出的一系列的高效诊断组件&#xff08;dotnet trace,dotnet sos,dotnet dump&#xff09;等, 这些工具提升了对程序Debug的能力和效率&#xff0c;可以让开发人员从更高层次的维度来…

android 强制下线功能,Android学习之基础知识八—Android广播机制实践(实现强制下线功能)...

强制下线功能算是比较常见的了&#xff0c;很多的应用程序都具备这个功能&#xff0c;比如你的QQ号在别处登录了&#xff0c;就会将你强制挤下线。实现强制下线功能的思路比较简单&#xff0c;只需要在界面上弹出一个对话框&#xff0c;让用户无法进行任何操作&#xff0c;必须…

USB权限的设置

USB设备给我们日常生活中带了很多的方便&#xff0c;能够在不同的不同的移动设备中传递数据。但也给我们的数据安全带来了隐患&#xff0c;它可以拷贝走我们计算机中很机密的信息&#xff01;通过下面的操作后&#xff0c;将使我们有用的信息增加更大安全性&#xff0c;至小也能…

漫谈高数——泰勒级数的物理意义

全世界有3.14 % 的人已经关注了数据与算法之美高等数学干吗要研宄级数问题&#xff1f;是为了把简单的问题弄复杂来表明自己的高深&#xff1f; No,是为了把各种简单的问题/复杂的问题&#xff0c;他们的求解过程用一种通用的方法来表示。提一个问题&#xff0c;99*99等于多少&…

保持学习,从这几个公众号开始!

全世界有3.14 % 的人已经关注了数据与算法之美关注了几百个公众号无目的的看文章却如鸡肋一般食之无味弃之可惜你是否也觉得时间被浪费&#xff1f;生命被辜负了&#xff1f;点击关注下面几个公众号它们每天更新有态度、有温度的原创文字每一篇推文都值得你点开长按二维码&…

回顾 | 在 GitHub 上贡献到开源项目

点击蓝字关注我们精彩回顾GitHub 是世界上最大的开源社区&#xff0c;拥有数百万个开源项目。GitHub 的优势之一是在项目上进行协作非常容易。在本研讨会中&#xff0c;我们将介绍在 GitHub 上找到开源项目并为其做出贡献的难易程度。我们将向您展示如何创建拉取请求&#xff0…

html assign无效,Object.assign的一些用法

Object.assign的一些用法2020/11/12 20:19:03  字体&#xff1a;大 中 小 浏览 1289 我要评论需要定制网站程序、公众号程序、微信小程序可以联系&#xff1a;565449214或者加微信13961347334(备注&#xff1a;需要技术)或者在处理一些技术问题时&#xff0c;有什么不懂的地…

R语言和 Python —— 一个错误的分裂

全世界有3.14 % 的人已经关注了数据与算法之美最近有一些文章提出与年龄相关的问题&#xff1a;“崭露头角的年轻数据科学家们是学习R语言还是Python更好?”答案似乎都是“视情况而定”&#xff0c;在现实中没有必要在R和Python中做出选择&#xff0c;因为你两个都用得到。推荐…

快速修改HTML5,HTML5无刷新修改URL(示例代码)

HTML5新添加了两个api分别是pushState和replaceState&#xff0c;DOM中的window对象通过window.history方法提供了对浏览器历史记录的读取&#xff0c;可以在用户的访问记录中前进和后退&#xff0c;我们可以开始操作这个历史记录堆栈。实例一、通过pushState修改URL通过这句代…

这一平台只要把握住风口期,自己就能当老板!

我是电商珠珠 短视频渐渐走进大家的视野&#xff0c;改变了大家的日常娱乐方式。从19年开始&#xff0c;抖音开始发展电商平台-抖音小店。 在改变大家娱乐方式的同时&#xff0c;还将直播电商的热度掀了起来&#xff0c;由此改变了大家的购物方式&#xff0c;给大家带来了方便…

如何用TensorFlow实现人工智能?

自 2015 年 11 月 9 号发布之后&#xff0c;TensorFlow 逐渐成为人工智能领域最广泛运用的深度学习框架。那么TensorFlow框架到底是什么&#xff1f;TensorFlow 是一个大规模机器学习的开源框架&#xff0c;提供了多种深度神经网络的支持。不仅 Google 在自己的产品线使用 Tens…

一不小心,知乎炸了!

阅读本文大概需要7分钟。昨天晚上加完班发完原创文章&#xff1a;如何成为一个搞垮公司的程序员&#xff1f;就准备回家了&#xff0c;突然发现知乎一堆咨询消息&#xff0c;看了看原来是之前回答的一个问题突然火了&#xff0c;就是这个&#xff1a;好家伙&#xff0c;一天不到…

[转载]Android Layout标签之-viewStub,requestFocus,merge,include

定义Android Layout(XML)时&#xff0c;有四个比较特别的标签是非常重要的&#xff0c;其中有三个是与资源复用有关&#xff0c;分别是<viewStub/>, <requestFocus />, <merge /> and<include />。可是以往我们所接触的案例或者官方文档的例子都没有着…

查询2021抚顺高考成绩,2021年抚顺高考状元是谁分数多少分,历年抚顺高考状元名单...

2020年抚顺一年一度的高考考试已经结束&#xff0c;今年抚顺高考状元是谁呢&#xff0c;抚顺高考状元出自哪个高中学校&#xff0c;文理科分数是多少分&#xff0c;一起来了解。一、2020年抚顺高考状元名单资料2020年抚顺高考状元名单和学校相关信息&#xff0c;截至目前发文时…

Android菜单详解——理解android中的Menu

前言 今天看了pro android 3中menu这一章&#xff0c;对Android的整个menu体系有了进一步的了解&#xff0c;故整理下笔记与大家分享。 PS&#xff1a;强烈推荐《Pro Android 3》&#xff0c;是我至今为止看到的最好的一本android书&#xff0c;中文版出到《精通Android 2》。 …

2021北京高考适应性测试成绩排名查询,2021届适应性考试学生成绩排名

1开启前照灯远光时仪表板上(如图所示)亮起。查看本题分析2在道路上跟车行驶时&#xff0c;跟车距离不是主要的&#xff0c;只须保持与前车相等的速度&#xff0c;即可防止发生追尾事故。查看本题分析3驾驶机动车在高速公路要按照限速标志标明的车速行驶。查看本题分析4这个标志…

.NET之Docker部署详细流程

开篇语自己从头开始走一遍docker部署.net的流程&#xff0c;作为一种学习总结&#xff0c;以及后续会写一些在该基础之上的文章。本次示例环境&#xff1a;vs2019、net5、docker、postman创建项目本次事例代码是用过vs2019创建的ASP.NET Core Web API项目image.png目标框架是.N…

平凡而又神奇的贝叶斯方法

全世界有3.14 % 的人已经关注了数据与算法之美概率论只不过是把常识用数学公式表达了出来。 ——拉普拉斯记得读本科的时候&#xff0c;最喜欢到城里的计算机书店里面去闲逛&#xff0c;一逛就是好几个小时&#xff1b;有一次&#xff0c;在书店看到一本…

d.ts文件可以注释html吗,如何编写一个d.ts文件的步骤详解_旧店_前端开发者

前言本文主要讲怎么写一个typescript的描述文件(以d.ts结尾的文件名&#xff0c;比如xxx.d.ts)。最近开始从之前也从网上面也找了一些资料&#xff0c;但还是看的云里雾里模糊不清&#xff0c;经过一段摸索&#xff0c;将摸索的结果记录下来&#xff0c;也希望可以给别人一个参…