【问题标题】:.Net app using 1.3GB on system with 16GB RAM throws OutOfMemoryException在具有 16GB RAM 的系统上使用 1.3GB 的 .Net 应用程序抛出 OutOfMemoryException
【发布时间】: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


【解决方案1】:

通常,RAM 并不有趣。 RAM 是指物理内存,您的应用程序不使用物理内存,它使用虚拟内存。您通常拥有比物理内存更多的虚拟内存,因为页面文件也算作虚拟内存。如您所见,WinDbg 报告有 127 TB 的可用内存。

但是,这并不意味着您实际上可以获得那么多内存。您的硬盘上需要 127 TB 的可用存储空间,这不太可能。您可以使用的典型内存在附近

your RAM size 
- kernel stuff 
- RAM in use by other applications
+ size of the page file
- page file use by other applications

事后计算该数量非常困难,因此我们无法真正知道 OOM 时有多少虚拟内存可用。

而且它仍然没有解释为什么在具有 16GB RAM 的系统上内存不足 1.3GB。

您可以使用更少的内存进行 OOM。尝试

#include <exception>
#include <limits>

int main()
{
   auto const p = malloc(std::numeric_limits<size_t>::max());
   if (!p) throw std::exception();
}

非托管内存的内存碎片?

Largest Region by Usage 说有一个 125 TB 的连续块。将请求一个新块来满足请求。当最大的可用块小于请求的块时,您会遇到碎片问题。

如果 GDI/用户句柄达到 8000,它会失败,远低于 10000 操作系统限制。

8000差不多了,恕我直言。请注意,不仅每个进程有 10000 个限制,还有 65536 per session

你能做什么?

【讨论】:

  • 仅仅使用 1.3GB 就意味着存在巨大的泄漏。我怀疑该应用程序正在做一些“聪明”的事情,比如将控件渲染到位图中,作为打印或生成文件的一种快速而肮脏的方式。
  • @PanagiotisKanavos 看到我上面的评论。我从事过几个较大的项目,其中在用户未执行任何操作的情况下内存使用量为 700 MB+。就在开始屏幕上。打开单个文档很容易使它翻倍。
  • @PanagiotisKanavos Visual Studio 使用 1.2 GB,即使像上面的 C++ 代码一样只有一个 5 行。 VS有泄漏吗?最肯定的是它在某处有泄漏,但没有那么多。下载 Paint.NET 并创建 30000x30000 位图。它将使用 3.6 GB 内存。它是否表明泄漏?可能不会。
  • 我从事过惰性对象管理的项目做过导致 1GB 的额外 RAM。有一个很多SO 问题已经存在 OOM 是由将大量项目逐一添加到 List<> 引起的。修复低效代码不仅减少了 RAM,还使性能提高了 4-8 倍
  • 至于导致 OOM 的 GDI,是的。 System.OutOfMemoryException: Out of memory (GDI)
猜你喜欢
  • 1970-01-01
  • 2016-12-30
  • 2012-12-20
  • 1970-01-01
  • 2016-01-04
  • 2012-04-15
  • 1970-01-01
  • 2010-12-13
  • 1970-01-01
相关资源
最近更新 更多