[英]'System.OutOfMemoryException' was thrown when there is still plenty of memory free
[英]System.OutOfMemoryException is thrown while still having free VM space
在詢問之前,我會做一個小小的免責聲明:是的,我知道虛擬內存,物理內存和工作集之間的區別。 以下所有數字均指虛擬內存。
情況如下:我們有一個32位C#應用程序,它導入x86 C ++庫(具有大量本機依賴性,因此目前無法遷移到x64)。 該應用程序通過非托管組件加載大型數據集,然后嘗試顯示它(報告)。
但是,當數據集特別大時,在向列表添加項目時會拋出OutOfMemory異常,如下面的代碼所示:
這里不足為奇。 然而,令人驚訝的是,該應用程序仍然擁有大約280MB的免費VM。
在調試純粹的非托管應用程序(C ++)時,情況從未如此 - 只有在沒有剩余空閑虛擬機的情況下,或者如果沒有足夠大小的空閑地址空間塊時,才能實現bad_alloc。
因此,問題 - 這可能是什么原因? 我理解如何解決這個問題 - 非托管組件確實會占用大量內存,並且會產生大量碎片 - 但是OutOfMemoryException出現這么早的原因是什么?
有問題的代碼如下所示:
List<Cell> r = new List<Cell>(cols);
for (int j = 0; j < cols; j++)
{
r.Add(new CustomCell()); // The exception is thrown on this line
}
在異常時刻,列表(在一個出現時)有85個項目,它的容量是200-something(列數,如構造函數中所示)。 因此,異常最有可能發生在CustomCell分配中。 CustomCell對象有很多字段,但總共不到1KB。 280MB的可用內存位於64KB到14MB的塊中 - 因此應該有足夠的空間來分配它。
有大量高達14Mb的塊。 失敗的操作是創建總共小於1KB的對象
14 MB肯定接近危險區域。 GC分配VM的方式與對象大小無關。 GC堆是從稱為“段”的VM塊創建的。 程序啟動時段大小為2 MB,但是當程序使用大量內存時,GC會動態增加新段的分配。 顯然你使用了大量的內存。 您無法做任何事情來影響VM分配或避免VM地址空間碎片。
顯然,您太接近32位進程的VM限制。 您需要徹底修改代碼,以便您可以少花錢。 或者您需要在您的先決條件列表中放置64位操作系統。 這可以為32位進程提供4千兆字節的VM地址空間。 您將需要額外的構建步驟來利用它,如本答案中所述 。
編輯
在添加代碼之前編寫。 看起來你已經這樣做了:
List
實例具有容量。
如果超出容量,則會調用不斷增長的算法,使列表的容量加倍。
//decompiled from List<T> private void EnsureCapacity(int min) { if (this._items.Length >= min) return; int num = this._items.Length == 0 ? 4 : this._items.Length * 2; if ((uint) num > 2146435071U) num = 2146435071; if (num < min) num = min; this.Capacity = num; //causes a copying of src array to a new array }
這可能導致意外的內存分配。
如果您能夠預測列表的最終大小,請預先分配並避免加倍:
var myList = new List<SomeType>(expectedCapacity)
當您有64位應用程序時,您還必須小心,如果要在數組中加載報表,則最大數組大小為2 GB。
更多信息: 關於大數組聲明的OutOfMemoryException
這也可能適合你: http ://social.msdn.microsoft.com/Forums/en-US/1a12abaa-50bd-4d28-b3c1-9de06a1488e9/how-to-create-an-extremely-large-arrayobject- 2-GB-而不-使用-鋸齒狀arrays-
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.