【发布时间】:2023-01-12 20:06:39
【问题描述】:
我有一个来自 Windows 10 64 位 .Net Winforms 应用程序的进程转储,该应用程序遇到了 System.OutOfMemoryException。转储文件为 1.3GB。托管分析器 (dotMemory) 表示分配了 220MB 的堆(其中 108MB 已使用)。
该应用程序编译为 AnyCPU,首选 32 位已关闭。它还包含面向 x64 的 CLI/C++ 项目,因此它不会在 32 位环境中运行。该应用程序在其他情况下愉快地使用了超过 1.3GB 的空间。
它在具有 16GB RAM 的系统上运行,那么为什么它会内存不足?
异常堆栈跟踪:
System.OutOfMemoryException: Out of memory.
at System.Drawing.Graphics.FromHdcInternal(IntPtr hdc)
at System.Drawing.Graphics.FromHdc(IntPtr hdc)
at DevExpress.XtraBars.Docking2010.DocumentsHost.DoPaint(Message& m)
at DevExpress.XtraBars.Docking2010.DocumentsHost.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
堆碎片可能是一回事。这是来自 dotMemory(托管内存)的报告,据我所知,无需担心:
WinDbg 给了我这个 '!address -summary'。虽然很多,但大多数只是“保留”而没有“承诺”。
0:000> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free 405 7ffe`8db96000 ( 127.994 TB) 100.00%
<unknown> 1515 1`3f3b3000 ( 4.988 GB) 86.22% 0.00%
Image 2261 0`25f26000 ( 607.148 MB) 10.25% 0.00%
Heap 120 0`08324000 ( 131.141 MB) 2.21% 0.00%
Stack 234 0`04bc0000 ( 75.750 MB) 1.28% 0.00%
Other 39 0`00200000 ( 2.000 MB) 0.03% 0.00%
TEB 78 0`0009c000 ( 624.000 kB) 0.01% 0.00%
PEB 1 0`00001000 ( 4.000 kB) 0.00% 0.00%
--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE 1882 1`452fe000 ( 5.081 GB) 87.82% 0.00%
MEM_IMAGE 2261 0`25f26000 ( 607.148 MB) 10.25% 0.00%
MEM_MAPPED 105 0`07236000 ( 114.211 MB) 1.93% 0.00%
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE 405 7ffe`8db96000 ( 127.994 TB) 100.00%
MEM_RESERVE 681 1`22426000 ( 4.535 GB) 78.39% 0.00%
MEM_COMMIT 3567 0`50034000 ( 1.250 GB) 21.61% 0.00%
--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE 1835 0`23d12000 ( 573.070 MB) 9.67% 0.00%
PAGE_EXECUTE_READ 195 0`15090000 ( 336.563 MB) 5.68% 0.00%
PAGE_READONLY 854 0`13fde000 ( 319.867 MB) 5.40% 0.00%
PAGE_WRITECOPY 484 0`01633000 ( 22.199 MB) 0.37% 0.00%
PAGE_EXECUTE_READWRITE 92 0`012db000 ( 18.855 MB) 0.32% 0.00%
PAGE_READWRITE|PAGE_WRITECOMBINE 5 0`00830000 ( 8.188 MB) 0.14% 0.00%
PAGE_READWRITE|PAGE_GUARD 78 0`0015e000 ( 1.367 MB) 0.02% 0.00%
PAGE_NOACCESS 24 0`00018000 ( 96.000 kB) 0.00% 0.00%
--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free 213`79810000 7de1`2f130000 ( 125.880 TB)
<unknown> 7ff4`a8af0000 1`00020000 ( 4.000 GB)
Image 7ffd`06181000 0`03b43000 ( 59.262 MB)
Heap 213`6b332000 0`0095d000 ( 9.363 MB)
Stack 52`c8600000 0`000fb000 (1004.000 kB)
Other 213`311e0000 0`00181000 ( 1.504 MB)
TEB 52`c8000000 0`00002000 ( 8.000 kB)
PEB 52`c8158000 0`00001000 ( 4.000 kB)
过度使用 GDI 句柄是导致问题的常见原因,但应用程序对此有一个看门狗:如果 GDI/用户句柄达到 8000,远低于 10000 OS 限制,它就会失败。
我也找到了这个bug report,但它在很久以前就被修复了。
在this post 中,一个典型的原因是 Bitmap 实例未被处置,因此它们的非托管内存堆积起来。但这应该会出现在 WinDbg 输出中。无论如何,我已经将应用程序的典型使用与错误行为进行了比较,它们都有大约 1000 个 Bitmap 实例(许多图标等)。它仍然可能是由一些非常大的未处理位图引起的,但这似乎不太可能。而且它仍然没有解释为什么在具有 16GB RAM 的系统上内存不足 1.3GB。
还有什么原因?非托管内存的内存碎片?我可以做些什么来进一步调查这个问题?
【问题讨论】:
-
你的代码是做什么的?在 99% 的情况下,OOM 是由于低效的应用程序代码导致的内存碎片而引发的,而不是缺少 RAM。例如,将项目逐一添加到列表中会导致内部缓冲区重新分配 log2(N) 次。 GDI 资源有其自身的限制,因此尝试使用 GDI 方法将 1000 个文档“呈现”为图像可能最终耗尽所有 GDI 资源。
-
The app happily uses more than 1.3GB in other circumstances.这几乎可以肯定意味着它正在泄漏。为什么任何应用程序需要首先是 1GB?不知何故,应用程序在 RAM 中分配了 1GB 的孤立对象,最终必须对其进行垃圾回收。这导致很多CPU 时间浪费在分配和 GC 孤儿上。修复泄漏可能会带来与应用程序并行化相似或更好的性能提升 -
@PanagiotisKanavos:使用所有GDI 资源不会导致OOM 异常,对吗?恕我直言,会发生很多奇怪的效果,比如黑框、屏幕不更新等。为什么应用程序不需要 1 GB?我从事过几个较大的项目,其中在用户未执行任何操作的情况下内存使用量为 700 MB+。
标签: c# .net out-of-memory