- A+
一:背景
1. 讲故事
前几天有位朋友找到我,说他的程序会偶发性的报 存储空间不足,无法处理此命令
的错误,让我帮忙看下到底怎么回事,哈哈,人家是有备而来,dump都准备好了,话不多说,直接分析开干。
二:WinDbg 分析
1. 捕获dump中的异常
一般来讲别人说的只是一个参考,我们需要自己到dump中去验证,可以用 !t
观察下。
0:000:x86> !t ThreadCount: 61 UnstartedThread: 0 BackgroundThread: 52 PendingThread: 0 DeadThread: 3 Hosted Runtime: no Lock ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 0 1 9310 004e24f8 26020 Preemptive 00000000:00000000 004d94e0 0 STA System.Runtime.InteropServices.COMException 42b57774 (nested exceptions) ... 0:000:x86> !PrintException /d 42b57774 Exception object: 42b57774 Exception type: System.Runtime.InteropServices.COMException Message: 存储空间不足,无法处理此命令。 (Exception from HRESULT: 0x80070008) InnerException: <none> StackTrace (generated): SP IP Function 00000000 00000001 mscorlib_ni!System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32, IntPtr)+0x2 003FAC0C 6F5655C9 mscorlib_ni!System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32, IntPtr)+0x9 003FAC10 55171671 PresentationCore_ni!MS.Internal.Text.TextInterface.Native.Util.ConvertHresultToException(Int32)+0x702961 003FAC24 54A56129 PresentationCore_ni!MS.Internal.Text.TextInterface.FontFace.GetDesignGlyphMetrics(UInt16*, UInt32, MS.Internal.Text.TextInterface.GlyphMetrics*)+0x79 003FAC60 54A73B77 PresentationCore_ni!System.Windows.Media.GlyphTypeface.GlyphMetrics(UInt16*, Int32, MS.Internal.Text.TextInterface.GlyphMetrics*, Double, System.Windows.Media.TextFormattingMode, Boolean)+0x47 ...
从卦中信息看确实抛了一个 COMException
异常,并且真的有这么一条错误信息 存储空间不足,无法处理此命令
,而且从调用栈来看貌似是wpf在处理 字形信息,一般来说这种代码是千锤百炼不会出任何问题的。
作为现代化的程序员,必须通过 百度搜索
寻找一下天涯沦落人,通过搜索得知大概有两种情况:
- 硬盘存储空间不足所致
- 内存不足所致
2. 是硬盘存储空间不足吗
要想验证是不是这种情况导致的,只能询问下朋友,据朋友反馈不存在这个问题,所以这条路就堵死了。
3. 是内存不足吗
要想排查这种情况只能观察进程的 MEM_COMMIT
指标,使用 !address -summary
。
0:000:x86> !address -summary ... --- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal MEM_COMMIT 3773 6e617000 ( 1.725 GB) 89.86% 86.24% MEM_RESERVE 702 c4c6000 ( 196.773 MB) 10.01% 9.61% MEM_FREE 667 5513000 ( 85.074 MB) 4.15% ... --- Largest Region by Usage ----------- Base Address -------- Region Size ---------- Heap32 23470000 fd0000 ( 15.812 MB) <unknown> 474b0000 19c4000 ( 25.766 MB) Image 2fbf1000 1180000 ( 17.500 MB) Free 7355b000 1a5000 ( 1.645 MB) Stack32 c50000 fd000 (1012.000 kB) Stack64 5a0000 39000 ( 228.000 kB) Other 8e0000 181000 ( 1.504 MB) TEB64 7ee37000 2000 ( 8.000 kB) Heap64 120000 65000 ( 404.000 kB) TEB32 7ee39000 1000 ( 4.000 kB) Other32 290000 1000 ( 4.000 kB) PEB64 7efdf000 1000 ( 4.000 kB) PEB32 7efde000 1000 ( 4.000 kB) ...
从卦中的信息看,当前程序提交内存是 1.7G
,看到这个值马上就想到了 2G虚拟地址
,那这个程序是不是x86的呢?除了观察内存地址,也可以通过观察 PE 头获知。
0:000:x86> lm start end module name 01380000 01450000 xxxWinApp C (no symbols) 0:000:x86> !dh xxxWinApp File Type: EXECUTABLE IMAGE FILE HEADER VALUES 14C machine (i386) 3 number of sections 654073AD time date stamp Tue Oct 31 11:25:33 2023 0 file pointer to symbol table 0 number of symbols E0 size of optional header 102 characteristics Executable 32 bit word machine
从卦中的 32 bit word machine
来看,确实没有开大地址,看样子是受到了 2G 的虚拟地址限制。
不过说实话我真的佩服写这个软件的程序员,在上限 2G 的空间内,能将程序控制在 1.72G
都不崩,把内存压得严严实实,确实??。
4. 如何开启大地址
开启大地址非常简单,可以用 DnSpy 打开我们的应用程序,然后勾选 Large Address Aware
选项,修改完之后进行保存,截图如下:
最后一个问题是开启后他的程序可以吃到 3G 还是 4G 呢?这个取决于是 x64 还是 x86 的操作系统,可以用 vertarget 观察。
0:000:x86> vertarget Windows 7 Version 7601 (Service Pack 1) MP (4 procs) Free x64 Product: WinNt, suite: SingleUserTS Edition build lab: kernel32.dll version: 6.1.7601.24545 (win7sp1_ldr_escrow.200102-1707) Debug session time: Mon Nov 27 11:10:18.000 2023 (UTC + 8:00) System Uptime: 5 days 8:49:46.984 Process Uptime: 1 days 0:50:00.000 Kernel time: 0 days 0:06:48.000 User time: 0 days 0:24:11.000
从卦中的 Windows 7 Version 7601 (Service Pack 1) MP (4 procs) Free x64
可知当前是 Windows7 x64 版本,即可以吃到 4G 内存,基本上就能解决这个问题。
三:总结
一般来说这种错误:存储空间不足,无法处理此命令
基本上 98% 都是内存空间不足导致的,只有 2% 的情况真的是 硬盘不足
。
这个dump最有价值的地方在于没有纠结于 COMException
异常,并且在没有抛出 OutofMemoryException 异常的前提前定位出问题所在。