记一次 .NET 某妇产医院 WPF内存溢出分析

一:背景

1. 讲故事

上个月有位朋友通过博客园的短消息找到我,说他的程序存在内存溢出情况,寻求如何解决。

2bcb13fea4ff0d24129ca7a93319f369.png

要解决还得通过 windbg 分析啦。

二:Windbg 分析

1. 为什么会内存溢出

大家都知道内存溢出对应着 .NET 中的 OutOfMemonryException 异常,这种异常有可能是托管代码手工抛出的,也有可能是CLR层面抛出的,言外之意就是可以通过两种方式排查。

  • 托管线程是否挂载着异常?

0:000> !t
ThreadCount:      23
UnstartedThread:  0
BackgroundThread: 5
PendingThread:    0
DeadThread:       17
Hosted Runtime:   noLock  ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception0    1 362c 00fac868     26020 Preemptive  7ED701A0:00000000 00fa6b60 0     STA 5    2 2d70 00fbeba0     2b220 Preemptive  7EBA7AC0:00000000 00fa6b60 0     MTA (Finalizer) 7    3 3264 061c8890   102a220 Preemptive  00000000:00000000 00fa6b60 0     MTA (Threadpool Worker) 17   15 3f98 19682b90   202b220 Preemptive  7EBB0830:00000000 00fa6b60 0     MTA 
XXXX   16    0 2845fb00     35820 Preemptive  00000000:00000000 00fa6b60 0     Ukn 18   14  a7c 2842b1c8   202b220 Preemptive  00000000:00000000 00fa6b60 0     MTA 
XXXX    6    0 2c9b3778   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX   18    0 288a1318   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX   23    0 288a22f0   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX   10    0 2ccf3550   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX   21    0 288a1860   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX   12    0 288a1da8   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX   11    0 2c993640   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX    8    0 2ccf3a98     35820 Preemptive  00000000:00000000 00fa6b60 0     Ukn 
XXXX    9    0 2ccf2030   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX    7    0 2c9aed88   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX   26    0 28898308   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX   25    0 2c492c68   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX    4    0 2c993b88   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX   20    0 2c9af2d0   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX   17    0 2c9afd60   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 
XXXX   24    0 2c9b1280   1039820 Preemptive  00000000:00000000 00fa6b60 0     Ukn (Threadpool Worker) 23   22 2658 2c9b02a8   1029220 Preemptive  7ED5BFF8:00000000 00fa6b60 0     MTA (Threadpool Worker)

从输出信息看,这些线程并没有挂载任何托管异常,我去。。。

  • 是否在 CLR 上抛出

这主要是看 托管堆(heap) 上的内存分配或者gc回收造成的内存不足,可以用 !ao 命令。

0:000> !ao
There was no managed OOM due to allocations on the GC heap

从输出信息看也没有任何异常,尴尬了😂😂😂。。。尼玛,那到底是因为什么呢?

2. 探索溢出原因

出现这种尴尬情况,我只能怀疑生成这个dump的时候并没有get到那个点,或者是我的知识边界有限,不过天无绝人之路,不在那个 也肯定在那个 附近,对吧,接下来用 !address -summary 看一下内存使用的归类信息。

0:000> !address -summary--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
<unknown>                              1520          4c185000 (   1.189 GB)  65.57%   59.45%
Image                                  4306          1f140000 ( 497.250 MB)  26.78%   24.28%
Free                                   1133           bf17000 ( 191.090 MB)            9.33%
Heap                                    617           7626000 ( 118.148 MB)   6.36%    5.77%
Stack                                    72           1740000 (  23.250 MB)   1.25%    1.14%
Other                                    34             7b000 ( 492.000 kB)   0.03%    0.02%
TEB                                      24             30000 ( 192.000 kB)   0.01%    0.01%
PEB                                       1              3000 (  12.000 kB)   0.00%    0.00%--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_MAPPED                              549          34b60000 ( 843.375 MB)  45.42%   41.18%
MEM_PRIVATE                            1718          20424000 ( 516.141 MB)  27.80%   25.20%
MEM_IMAGE                              4307          1f155000 ( 497.332 MB)  26.78%   24.28%--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_COMMIT                             4904          66ddd000 (   1.607 GB)  88.64%   80.37%
MEM_RESERVE                            1670           d2fc000 ( 210.984 MB)  11.36%   10.30%
MEM_FREE                               1133           bf17000 ( 191.090 MB)            9.33%--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READONLY                          2272          382cf000 ( 898.809 MB)  48.41%   43.89%
PAGE_READWRITE                         1572          1eead000 ( 494.676 MB)  26.64%   24.15%
PAGE_EXECUTE_READ                       218           dd59000 ( 221.348 MB)  11.92%   10.81%
PAGE_WRITECOPY                          449           133e000 (  19.242 MB)   1.04%    0.94%
PAGE_EXECUTE_READWRITE                  188            ab4000 (  10.703 MB)   0.58%    0.52%
PAGE_NOACCESS                           156             9c000 ( 624.000 kB)   0.03%    0.03%
PAGE_READWRITE | PAGE_GUARD              48             78000 ( 480.000 kB)   0.03%    0.02%
PAGE_READWRITE | PAGE_WRITECOMBINE        1              2000 (   8.000 kB)   0.00%    0.00%--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
<unknown>                                   1d200000           a001000 ( 160.004 MB)
Image                                        fed1000           36e4000 (  54.891 MB)
Free                                        33dfe000           1082000 (  16.508 MB)
Heap                                        3da84000            a1b000 (  10.105 MB)
Stack                                        1a10000             fd000 (1012.000 kB)
Other                                       7fa40000             33000 ( 204.000 kB)
TEB                                           a4c000              3000 (  12.000 kB)
PEB                                           a3d000              3000 (  12.000 kB)

从上面的 MEM_COMMIT=1.607 GB 80.37% 信息看,当前内存占用 1.6G,占比 80.37%,可以看出它受到了一个 2G内存 的限制,而且从 !t 输出中的内存地址看,当前是 32bit 程序,所以这是一个经典的: 64系统跑着32位程序被2G内存限制 的问题。

3. 如何突破 2G 限制

要寻找答案,还得看最权威的 MSDN: https://docs.microsoft.com/en-us/windows/win32/memory/memory-limits-for-windows-releases?redirectedfrom=MSDN

破局 还得设置程序的 IMAGE_FILE_LARGE_ADDRESS_AWARE 标记。

ae13f84d06c8bbe472d349ecbb65944c.png

关于具体怎么设置,我找了三种方法。

  • 使用 LargeAddressAware 安装包

29a90f409e5948ec2d03814faf12c8ac.png

参见 github:https://github.com/KirillOsenkov/LargeAddressAware

  • 使用 editbin

可以在 vs 的生成事件中输入 editbin /largeaddressaware $(TargetPath)

  • 使用代码方式

这种可以直接给生成好的 exe 增加 LargeAddressAware 标记,除了标记,还能检测,🐂👃

using System;
using System.IO;namespace PEFile
{public class LargeAddressAware{public static bool IsLargeAddressAware(string filePath){bool isLargeAddressAware = false;PrepareStream(filePath, (stream, binaryReader) => isLargeAddressAware = (binaryReader.ReadInt16() & 0x20) != 0);return isLargeAddressAware;}public static void SetLargeAddressAware(string filePath){PrepareStream(filePath, (stream, binaryReader) =>{var value = binaryReader.ReadInt16();if ((value & 0x20) == 0){value = (short)(value | 0x20);stream.Position -= 2;var binaryWriter = new BinaryWriter(stream);binaryWriter.Write(value);binaryWriter.Flush();}});}private static void PrepareStream(string filePath, Action<Stream, BinaryReader> action){using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read)){if (stream.Length < 0x3C){return;}var binaryReader = new BinaryReader(stream);// MZ headerif (binaryReader.ReadInt16() != 0x5A4D){return;}stream.Position = 0x3C;var peHeaderLocation = binaryReader.ReadInt32();stream.Position = peHeaderLocation;// PE headerif (binaryReader.ReadInt32() != 0x4550){return;}stream.Position += 0x12;action(stream, binaryReader);}}}
}

更多办法参考:https://stackoverflow.com/questions/639540/how-much-memory-can-a-32-bit-process-access-on-a-64-bit-operating-system

三:总结

总的来说,2G 内存限制 是一个 32bit 程序所必须面对的问题,知道了就好解决了,最后有一个问题要解释下,为什么 commit 内存高达 1.6G,这是因为医疗类的软件,大多是 FastReport + DevExpress 这些重量级的经典搭配以及大量的图片资源占用了太多 native memory。

END

工作中的你,是否已遇到 ... 

1. CPU爆高

2. 内存暴涨

3. 资源泄漏

4. 崩溃死锁

5. 程序呆滞

等紧急事件,全公司都指望着你能解决...  危难时刻才能展现你的技术价值,作为专注于.NET高级调试的技术博主,欢迎微信搜索: 一线码农聊技术,免费协助你分析Dump文件,希望我能将你的踩坑经验分享给更多的人。

4e733fbf915b5c7b6016606bf7b4d956.png

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

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

相关文章

16世纪的旷世奇才:大学弃医丛数,仅用20年就独立发明了温度计、军事罗盘、天文望远镜,后半生双目失明还能写出惊人科学著作

全世界只有3.14 % 的人关注了爆炸吧知识传说&#xff0c;在崇尚绝对权威的中世纪里&#xff0c;有这么一位敢于质疑权威的年轻人。在比萨斜塔上做了“两个铁球同时落地”的实验&#xff0c;得出了重量不同的两个铁球同时下落的结论。从此推翻了亚里士多德“物体下落速度和重量成…

禅道项目管理软件介绍

使用流程 一、分享的流程图 二、流程图 维护产品及模块 一、如何来添加产品呢&#xff1f;让我们来看下步骤&#xff1a; 以管理员或者其他有产品管理权限的帐号登录。点击产品视图。在页面右侧&#xff0c;点击“新增产品”&#xff0c;即可出来产品添加页面。&#xff08;第一…

轻松学PHP编程 源代码

http://pan.baidu.com/share/link?shareid170353&uk1191536722 转载于:https://blog.51cto.com/letianwuji/1106854

java jar包 平滑重启,nginx 平滑重启的实现方法

一、背景在服务器开发过程中&#xff0c;难免需要重启服务加载新的代码或配置&#xff0c;如果能够保证server重启的过程中服务不间断&#xff0c;那重启对于业务的影响可以降为0。最近调研了一下nginx平滑重启&#xff0c;觉得很有意思&#xff0c;记录下来供有兴趣的同学查阅…

Android之图形图像之使用Path类总结

Path类可以预先在View上将N个点连成一条"路径",然后调用Canvas的drawPath(path,paint)即可沿着路径绘制图形 Android还为路径绘制提供了PathEffect来定义绘制效果,PathEffect包含如下子类 ComposePathEffect CornerPathEffect DashPathEffect DiscretePathEffect Pat…

[ACM_图论] The Perfect Stall 完美的牛栏(匈牙利算法、最大二分匹配)

描述 农夫约翰上个星期刚刚建好了他的新牛棚&#xff0c;他使用了最新的挤奶技术。不幸的是&#xff0c;由于工程问题&#xff0c;每个牛栏都不一样。第一个星期&#xff0c;农夫约翰随便地让奶牛们进入牛栏&#xff0c;但是问题很快地显露出来&#xff1a;每头奶牛都只愿意在她…

神奇的交际圈!这位17世纪的法国神父结交的好朋友,竟然都是一流数学牛人:笛卡尔、费马、加森迪······

全世界只有3.14 % 的人关注了爆炸吧知识话说&#xff0c;在近代数学史上&#xff0c;人们惊讶地发现17至18世纪的法国竟然产生了众多一流的数学家。然而&#xff0c;最早想到要培养一波优秀人才的&#xff0c;成就这段群星璀璨的传奇历史&#xff0c;并非是君王。而是出自于一位…

[原创]FineUI秘密花园(二十七) — 窗体控件概述(上)

窗体控件在项目中使用非常频繁&#xff0c;同时窗体控件和启用IFrame的面板控件也一起构成了FineUI所特有的内联框架&#xff0c;从而使弹出窗体不再局限于IFrame页面中。本章我们会详细介绍窗体控件的基本用法。 创建窗体控件 在页面中声明窗体控件标签&#xff0c;并设置需要…

Android之AIDL使用详解

1.什么是aidl:aidl是 Android Interface definition language的缩写&#xff0c;一看就明白&#xff0c;它是一种android内部进程通信接口的描述语言&#xff0c;通过它我们可以定义进程间的通信接口 icp:interprocess communication :内部进程通信 2.既然aidl可以定义并实现进…

用php打印九九乘法表,php如何打印出九九乘法表呢?

摘要:下文讲述使用php代码在页面上输出九九乘法表的示例分享&#xff0c;如下所示&#xff1b;实现思路:主要使用遍历的方式输出九九乘法表例:php 循环输出九九乘法表echo "maomao365.com 示例分享\n";echo "for 循环打印出九九乘法表";for($j1; $j<9; …

他言行不一屡次跳槽,还升职加薪走上了人生巅峰,全数学界都炸了......

全世界只有3.14 % 的人关注了爆炸吧知识看在大家都这么爱学习的份上&#xff0c;今天小天就和大家介绍一名老师吧&#xff01;这位老师就是亚历山大.雅科夫列奇.辛钦&#xff01;家庭教育好&#xff0c;学霸就有了亚历山大.雅科夫列奇.辛钦&#xff0c;1894年出生于前苏联莫斯科…

IoTSharp部署教程-Sqlite分表篇

IoTSharp的部署环境极其灵活&#xff0c;我们推荐使用Docker方式进行部署&#xff0c; 这减少了很多配置&#xff0c; 首先最简单的部署当然属于 Sqlite 数据库&#xff0c; 且不适用任何外部依赖。 在生产环境中&#xff0c; 我们需要配置 appsettings.Production.json 文件&a…

Android之AsyncTask两种线程池分析和总结

Android AsyncTask两种线程池分析和总结(一) 前言在android AsyncTask里面有两种线程池供我们调用1&#xff0e; THREAD_POOL_EXECUTOR, 异步线程池2&#xff0e; SERIAL_EXECUTOR&#xff0c;同步线程池正如上面名称描述的那样&#xff0c;一个是异步线程池&#xff…

一款好的折线图、饼图、柱形图

链接地址http://www.highcharts.com/demo/line-basic/skies http://www.highcharts.com/demo/转载于:https://www.cnblogs.com/blueking/p/3530787.html

被遗忘的数学家!曾提出最接地气的数学定理,可以计算男朋友真不真心的那种......

全世界只有3.14 % 的人关注了爆炸吧知识在介绍了业余数学家费马后&#xff0c;听说很多小伙伴还想看看业余的。这不&#xff0c;小天这次又来介绍业余数学家来了。险些被遗忘托马斯.贝叶斯&#xff0c;十八世纪英国的一个长老会的牧师&#xff08;专业&#xff09;和数学家&…

Android之MVVM框架 - 数据绑定

本教程是跟着 Data Binding Guide 学习过程中得出的一些实践经验&#xff0c;同时修改了官方教程的一些错误&#xff0c;每一个知识点都有对应的源码&#xff0c;争取做到实践与理论相结合。 Data Binding 解决了 Android UI 编程中的一个痛点&#xff0c;官方原生支持 MVVM 模…

再见 Typora,这款 Markdown 编辑器开源又免费!

推荐一个免费开源的 Markdown 编辑器编程导航 致力于推荐优质编程资源 &#x1f48e;项目开源仓库&#xff1a;https://github.com/liyupi/code-nav跪求一个 star ⭐️哈喽大家好&#xff01;我是编程导航的小编火宝。前段时间 Typora 宣布将升级并开始收费&#xff0c;想必大家…

Android窗口管理服务WindowManagerService的简要介绍和学习计划

在前一个系列文章中&#xff0c;我们从个体的角度来分析了Android应用程序窗口的实现框架。事实上&#xff0c;如果我们从整体的角度来看&#xff0c;Android应用程序窗口的实现要更复杂&#xff0c;因为它们的类型和作用不同&#xff0c;且会相互影响。在Android系统中&#x…

Andorid之为何要用到NDK?

概括来说主要分为以下几种情况&#xff1a; 1. 代码的保护&#xff0c;由于apk的java层代码很容易被反编译&#xff0c;而C/C库反汇难度较大。 2. 在NDK中调用第三方C/C库&#xff0c;因为大部分的开源库都是用C/C代码编写的。 3. 便于移植&#xff0c;用C/C写得库可以方便在其…

日本最惨数学天才!37岁裸辞,房子被政府没收,向全村人乞讨,一家五口只能吃野菜.........

全世界只有3.14 % 的人关注了爆炸吧知识数学是火他是飞蛾多年以后&#xff0c;当冈熙哉站在桥本市的数学家纪念碑前&#xff0c;准会想起父亲请他吃面包片的那个遥远的夜晚。当时&#xff0c;他们一家五口蜗居在邻居施舍租出的小库房里。冈洁家三代同堂在他的回忆中&#xff0c…