二:Windbg 分析
1. 为什么会内存溢出
大家都知道内存溢出对应着 .NET 中的
OutOfMemonryException
异常,这种异常有可能是托管代码手工抛出的,也有可能是CLR层面抛出的,言外之意就是可以通过两种方式排查。
0:000> !t
ThreadCount: 23
UnstartedThread: 0
BackgroundThread: 5
PendingThread: 0
DeadThread: 17
Hosted Runtime: no
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 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)
从输出信息看,这些线程并没有挂载任何托管异常,我去。。。
这主要是看
托管堆(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
标记。
关于具体怎么设置,我找了三种方法。
参见 github:https://github.com/KirillOsenkov/LargeAddressAware
可以在 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 header
if (binaryReader.ReadInt16() != 0x5A4D)
return;
stream.Position = 0x3C;
var peHeaderLocation = binaryReader.ReadInt32();
stream.Position = peHeaderLocation;
// PE header
if (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。
工作中的你,是否已遇到 ...
1. CPU爆高
2. 内存暴涨
3. 资源泄漏
4. 崩溃死锁
5. 程序呆滞
等紧急事件,全公司都指望着你能解决... 危难时刻才能展现你的技术价值,作为专注于.NET高级调试的技术博主,欢迎微信搜索: 一线码农聊技术,免费协助你分析Dump文件,希望我能将你的踩坑经验分享给更多的人。
1、剪切(clipping)
大部分面板都有一个Boolean的属性ClipToBounds并且它们的值都设为true,所以只要是溢出面板的都会被剪切掉
只有Canvas面板的这个属性可以设置,并且ClipToBounds的值默认是false。
2、滚屏(scrolling)
只需将所有的元素(或面板)放入到一个<ScrollViewer>.....&...
本文内存优化主要涉及以下几个方面这几点中首先要保证不要出现内存泄漏,开发过程中尽量注意节省内存,至于使用虚拟内存这个方式并不建议,因为其实它只是减少了内存条的内存占用而使用了硬盘虚拟的内存,在任务管理器中内存确实占用少了,但是其实上并没少,只要超过了程序的最大使用内存依旧会崩溃,表现就是任务管理器中明明显示的内存占用不高,但是程序却报内存不足的错误,这是自欺欺人的办法,也不利于排查问题,但是如果程序本身部分内存占用长时间不用,并且程序本身没有内存泄漏,倒是可以用这种方法减少物理内存的使用。...
在我们日常生活中,使用电脑的时候,经常可能会被提升电脑内存不足。而往往提到这种情况时,除了优化系统和软件以外,更多的则是直接从硬件入手,也就是添加内存条,避免再出现内存不足的情况。但又有些时候,明明都已经拓展增加内存了,但为何还是提示内存不足呢?近日,英特尔官方的专家,为用户回答了这个问题。他们表示这种情况,极有可能是某个程度的代码错误导致电脑内存溢出,之前被占用的内存空间无法得到正确释放。一般用...
在信息安全和编程中,缓冲区溢出是一种异常,其中程序在将数据写入缓冲区时会超出缓冲区边界并覆盖相邻的内存位置。缓冲区是留出的用于存储数据的内存区域,通常是在将数据从程序的一个部分移动到另一部分或在程序之间移动时使用的。如果假设所有输入都小于特定大小,并且缓冲区被创建为该大小,则产生更多数据的异常事务可能导致其写入缓冲区的末尾。缓冲区溢出概念缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过...
上个月有位朋友找到我,说他的程序存在内存溢出情况,寻求如何解决。要解决还得通过 windbg 分析啦。大家都知道内存溢出对应着 .NET 中的 OutOfMemoryException 异常,这种异常有可能是托管代码手工抛出的,也有可能是CLR层面抛出的,言外之意就是可以通过两种方式排查。1234567891011121314151617181920212223242526272829303132从输出信息看,这些线程并没有挂载任何托管异常,我去。。。这主要是看 托管堆(heap) 上的内存分配或者gc回收
frame控件相当于一个嵌套在程序里的浏览器一样,它可以展示page类实例,拥有导航UI,可以通过NavigationUIVisibility属性来控制导航UI的可见性。
在做应用程序开发的时候发现,我把NavigationUIVisibility设为hidden了,在程序多点几个按钮是发现内存在不断上涨。(按钮的左右是展示不同page),后来发现,是frame控件在作怪,当一个新的page...
stackoverflow again! 编译器什么时候会生成检查栈的代码呢?_chkstk 内部都做了哪些事呢?该如何避免呢?本文以一个实际项目中的栈溢出为出发点,尽量挖掘出栈溢出背后的一些逻辑。欢迎留言交流!