疑难解答ERROR_NOT_ENOUGH_MEMORY

问题描述:

我们的应用程序在ERROR_NOT_ENOUGH_MEMORY(“没有足够的存储可用于处理此命令”)的特定用户计算机上失败。疑难解答ERROR_NOT_ENOUGH_MEMORY

这个错误显然是在我们正在使用的Delphi VCL框架的深处提出的,所以我不确定哪个Windows API函数是负责任的。

记忆是个问题吗?GlobalMemoryStatus的呼叫提供了以下信息:

  • dwTotalPhys - 1063150000(〜1 GB)
  • dwAvailPhys - 26735000(〜27 MB)
  • dwAvailPage - 14.89亿(〜1.4 GB)

对于我来说,在分页文件中有太多可用空间时,Windows会让可用物理内存变得如此之低,但我对Windows的虚拟内存管理知之甚少,无法确定这是否正常。是吗?

如果不是内存,那么哪个资源限制被击中?根据我在线阅读的内容,ERROR_NOT_ENOUGH_MEMORY可能是由于应用程序触及了几个限制(GDI对象,USER对象,句柄等)而不是内存中的任何一个。有没有Windows的强制限制的完整列表?有什么方法可以找出哪个极限正在被击中?我尝试了谷歌,但我找不到任何系统的概述。

+0

GDI是我的第一个停靠港。虽然奇怪的是它只发生在一台用户的机器上,但这也是我开始的地方。它也可能是一些类似于正在使用的手柄等等。祝你好运! – 2009-09-21 14:22:29

此案的罪魁祸首是CreateCompatibleBitmap。显然,Windows可能会对设备相关位图的内存执行相当严格的系统范围限制(请参阅,例如,this mailing list discussion),即使您的系统拥有大量内存和大量GDI资源。 (这些系统范围的限制显然是因为Windows可以在显卡的内存分配依赖于设备的位图。)

解决方法是使用设备无关的位图(的DIB),而不是(虽然这些可能不提供相当良好的性能)。 This KB article描述了如何为设备选择最佳的DIB格式。

资源限制其他候选人(从别人的答案和我自己的研究):

  • GDI资源(从这个答案) - 与GDIView
  • 虚拟内存碎片很容易地检查(从这个答案)
  • 桌面堆 - 见herehere

比你列出的任何一个更常见的原因是这个错误是虚拟内存空间的碎片。这种情况下,尽管总空闲内存非常合理,但空闲空间被当前分配的各种虚拟内存空间碎片化。因此,当内存请求无法通过单个连续块满足时,即使总共有足够的空闲空间,也可能出现内存不足错误。

+0

这应该相当容易通过尝试从全新的重新启动相同的事情来检查。有人会认为他们会在将它发布为SO的问题之前尝试过(但是事情已经发生了......) – 2009-09-21 14:55:43

+0

@ T.E.D。 :重启并不比简单地查杀错误进程并再次尝试更有效。每个进程在首次创建时都有自己的新虚拟内存空间。这个问题通常在一个长时间运行的应用程序中自然产生,或者在存在大量和小块内存的分配和快速分配以及不同生命周期的情况下很快出现。它通常指示某些代码块应该保留一个缓冲区列表以供重用,而不是不断分配和释放内存。 – AnthonyWJones 2009-09-21 15:17:42

+0

在Delphi中使用FastMM可以更好地防止虚拟内存碎片。 – 2011-02-04 19:47:53

检查所有可能性。

可以使用免费的GDIView实用程序监控GDI问题。它是一个单独的文件,用户无需安装程序即可启动。

另外,在相关机器上安装ProcessExplorer

如果您无法访问机器,请让用户截取应用程序监控的状态。非常喜欢,这会给你一些提示。

+0

GDIView给出有趣令人印象深刻有关“GDI对象”的详细信息。 – Wolf 2015-01-27 14:01:41

我的答案可能有点晚,但是,从我的已故Ë经验与同样的问题,做所有的测试,一步一步,创建DC,释放它,使用DIBSection而不是CompatibleBitmap,使用泄漏GDI /内存工具等。

最后(LOL)我发现:

我正在切换这两个调用的优先级,然后整个问题就解决了。

DeleteDC(hdc);  //do it first (always before deleting objects) 
DeleteObject(obj);