2009-10-24 49 views
0

我有以下問題(?):超出內存在Win32(與Linux)的問題

在Windows機器上的程序運行(32位,3.1GB的內存,無論是VC++ 2008和MinGW編譯代碼)失敗,拋出一個bad_alloc異常(在分配大約1.2Gb後;嘗試分配一個900萬雙打的矢量,即大約75Mb的矢量時拋出異常),並有大量RAM仍然可用(至少根據任務管理器)。

在Linux機器上運行的相同程序(32位,4Gb內存; 32位,2Gb內存)運行良好,峯值內存使用量約爲1.6Gb。有趣的是,由mingw運行在wine下的4Gb linux機器上的win32代碼在bad_alloc下也失敗,儘管在不同的(稍後)地方,然後當在windows下運行時...

有什麼可能的問題?

  • 堆碎片? (我怎麼知道?這怎麼解決?)
  • 堆腐敗? (我已經啓用了pageheap.exe的代碼,沒有報告錯誤;實現了帶邊界檢查的向量訪問---再次沒有錯誤;代碼本質上沒有指針,只使用std::vector s和std::list s。在Valgrind(memcheck)下消耗太多內存並提前結束,但沒有發現任何錯誤)
  • 內存不足? (應該有足夠的內存)

此外,可能是什麼,雖然 Linux版本的工作(甚至在較小的內存的機器)的Windows版本失敗的原因嗎? (另請注意, /LARGEADDRESSAWARE鏈接器標誌使用VC + 2008年如果能夠有任何影響)

任何想法,將不勝感激,我在我無計可施這樣結束...... :-(

+0

我注意到我實際上在不斷調整可能導致碎片的矢量大小。試圖解決這個問題,但它似乎沒有預期的效果,但我可能錯過了一些東西。當然有些東西需要調查(即sysinternals輸出)。當我知道更多的時候會回來...... –

+0

事實證明,堆碎片是罪魁禍首。我能夠消除大部分的矢量調整大小。然而,問題仍然存在,因爲構建一個大型的(大約900萬行)std :: list向量會立即導致程序失效。我想我必須爲列表實現一個自定義分配器(我對此不太瞭解),或者將列表的實現切換爲固定大小的數組(我的列表很小,所以我不會因此釋放太多內存) 。有趣的是,當用mingw編譯時,該程序現在可以適應2GB的容量,而VC不能。 –

回答

0

您是否正在編譯Debug模式?如果是這樣,分配會產生大量的調試數據,這可能會產生您看到的錯誤,出現真正的內存不足問題。請嘗試Release以查看是否解決了問題

我只用過VC,而不是MinGW,但後來我也沒有選中,這仍然可以解釋問題

+0

嘗試釋放,釋放調試信息,調試無效 –

+0

我不明白爲什麼在'調試'模式下編譯分配會產生大量的調試數據?如果您正在使用調試分配器,則會在分配的塊周圍使用幾個額外的字節來跟蹤它,並且額外的CPU週期將被刻錄以寫入並檢查空閒內存中的特定位模式。除此之外,它不應該影響內存分配的方式。 –

+0

@Martin,就是我用Visual C編寫過雙陣列的經歷,我沒有檢查微軟是如何管理它的。症狀是完全相同的,這是一個非常快速的測試,所以值得一提的是,即使最終它在這種情況下不適用於OP。 – RedGlyph

5

它與系統中有多少RAM無關。虛擬地址空間不足。對於32位Windows操作系統進程,用戶模式(3GB的情況下爲LARGEADDRESSAWARE)和內核2GB的虛擬地址空間(不論您使用多少RAM)爲2GB。當您嘗試使用new分配內存時,OS將嘗試查找足夠大以滿足內存分配請求的虛擬內存連續區塊。如果你的虛擬地址空間碎片嚴重,或者你正在尋求一塊巨大的內存,那麼它會拋出一個bad_alloc異常。檢查您的進程正在使用多少虛擬內存。

+0

這也是一個好點,在這種情況下虛擬內存纔是真正重要的。 – RedGlyph

+0

感謝您的回覆!如何找出進程正在使用的虛擬內存?任務管理器表示,該程序在失敗時使用1.2Gb。這應該遠低於2GB標記(我正在與LARGEADDRESSAWARE鏈接)。那時該程序正在嘗試僅分配大約75Mb ...有沒有辦法知道地址空間是否碎片嚴重?如何避免? –

+0

嘗試在任務管理器中啓用一個名爲類似於XP上的「虛擬內存」的列,以及類似於vistas上的「私人(?)字節」的列。還可以考慮使用sysinternals(AKA Rusinovich)中的進程管理器,它比任務管理器優越得多。就像「perfmon」(我相信它帶有windows)一樣,也可以幫助您瞭解機器內存到底發生了什麼。 – Dmitry

2

對於Windows XP x86和默認設置,1.2 GB是關於在系統庫,代碼,堆棧和其他內容獲得共享之後爲堆留下的所有地址空間。請注意,largeaddressaware要求您使用/ 3GB引導標誌進行引導,以嘗試將進程升級到3GB。/3GB標誌會導致很多XP系統不穩定,這就是默認情況下未啓用的原因。

Windows x86的服務器變體通過使用3GB/1GB拆分和使用PAE允許使用全部4GB內存,爲您提供更多地址空間。

Linux x86默認使用3GB/1GB拆分。

一個64位操作系統會給你更多的地址空間,即使是一個32位的進程。

+0

嗯,感謝/ 3GB啓動標誌的解釋,我沒有意識到這一點。 Linux的3/1分裂似乎表明程序爲什麼在Linux下運行。 –

0

詳細說明虛擬內存: 當您嘗試分配單個90MB陣列時,您的應用程序會失敗,並且虛擬內存沒有連續的空間可放置。如果切換到使用較少內存的數據結構,可能會稍微遠一些 - 也許某些類使用樹(其中所有數據都保存在1MB左右的葉節點中)近似於一個巨大的數組。另外,在C++下執行大量分配時,如果所有這些大分配的大小相同,這確實有所幫助,這有助於重複使用內存並將碎片保持得更低。

但是,從長遠來看,正確的做法是簡單地切換到64位系統。

+0

事實證明,堆碎片可能是問題。 –