2009-09-21 398 views
5

我們的應用程序在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

回答

3

此案的罪魁禍首是CreateCompatibleBitmap。顯然,Windows可能會對設備相關位圖的內存執行相當嚴格的系統範圍限制(請參閱,例如,this mailing list discussion),即使您的系統擁有大量內存和大量GDI資源。 (這些系統範圍的限制顯然是因爲Windows可以在顯卡的內存分配依賴於設備的位圖。)

解決方法是使用設備無關的位圖(的DIB),而不是(雖然這些可能不提供相當良好的性能)。 This KB article描述瞭如何爲設備選擇最佳的DIB格式。

資源限制其他候選人(從別人的答案和我自己的研究):

  • GDI資源(從這個答案) - 與GDIView
  • 虛擬內存碎片很容易地檢查(從這個答案)
  • 桌面堆 - 見herehere
3

比你列出的任何一個更常見的原因是這個錯誤是虛擬內存空間的碎片。這種情況下,儘管總空閒內存非常合理,但空閒空間被當前分配的各種虛擬內存空間碎片化。因此,當內存請求無法通過單個連續塊滿足時,即使總共有足夠的空閒空間,也可能出現內存不足錯誤。

+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

4

檢查所有可能性。

可以使用免費的GDIView實用程序監控GDI問題。它是一個單獨的文件,用戶無需安裝程序即可啓動。

另外,在相關機器上安裝ProcessExplorer

如果您無法訪問機器,請讓用戶截取應用程序監控的狀態。非常喜歡,這會給你一些提示。

+0

GDIView給出有趣令人印象深刻有關「GDI對象」的詳細信息。 – Wolf 2015-01-27 14:01:41

0

我的答案可能有點晚,但是,從我的已故Ë經驗與同樣的問題,做所有的測試,一步一步,創建DC,釋放它,使用DIBSection而不是CompatibleBitmap,使用泄漏GDI /內存工具等。

最後(LOL)我發現:

我正在切換這兩個調用的優先級,然後整個問題就解決了。

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