2013-03-28 49 views
5

我正在運行一些測試,看我的日誌記錄將如何執行,而不是做File.AppendAllText我會先寫入內存流,然後複製到文件。所以,只是爲了看看如何快速記憶操作我這樣做..即使有大約700Mb的RAM空間,爲什麼我會得到System.OutOfMemoryException?

private void button1_Click(object sender, EventArgs e) 
    { 
     using (var memFile = new System.IO.MemoryStream()) 
     { 
      using (var bw = new System.IO.BinaryWriter(memFile)) 
      { 
       for (int i = 0; i < Int32.MaxValue; i++) 
       { 
        bw.Write(i.ToString() + Environment.NewLine); 
       } 
       bw.Flush(); 
      } 
      memFile.CopyTo(new System.IO.FileStream(System.IO.Path.Combine("C", "memWriteWithBinaryTest.log"), System.IO.FileMode.OpenOrCreate)); 
     } 
    } 

i達到25413324我得到了Exception of type 'System.OutOfMemoryException' was thrown.即使我Process Explorer說,我對自由的RAM 700MB ???

下面是屏幕截圖(以防萬一)

進程瀏覽器 enter image description here

這裏的WinForm的

enter image description here

編輯:對於多個對象的緣故被創建在堆上,我將bw.write重寫爲這個

bw.Write(i); 
+0

根據回答,'BinaryWriter'不寫出數字的字符串表示形式。它將寫入字節表示。也許你想要後者,但從原來的代碼它並不出現如此。 – leppie 2013-03-28 11:21:08

回答

9

首先,內存不足,因爲您在MemoryStream中累積了數據,​​而不是直接將其寫入FileStream。直接使用FileStream,你根本不需要太多的RAM(但你必須保持文件打開)。

未使用的物理內存量與此異常沒有直接關係,這聽起來很奇怪。

重要的是:

  • ,你有內存中的進程虛擬地址空間
  • ,該系統提交不超過總RAM大小+頁面文件大小提供一個連續的大塊

當你問Windows內存管理器來分配你一些RAM,需要檢查沒有多少可用的,但它擁有多少承諾爲每個其他進程提供。這種承諾是通過承諾完成的。到提交某些內存意味着內存管理器爲您提供了一個保證,當您最終使用它時,它將會提供可用。

因此,它可能是物理RAM已完全耗盡,但您的分配請求仍然成功。爲什麼?因爲頁面文件中有很多空間可用。當你真的開始使用你通過這樣的分配獲得的RAM時,內存管理器將只是簡單地分頁出其他東西。所以0物理RAM!=分配將失敗。

相反也可能發生;即使有一些未使用的物理RAM,分配也會失敗。你的進程通過所謂的虛擬地址空間來看內存。當你的進程讀取地址爲0x12340000的內存時,這是一個虛擬地址。它可能映射到RAM爲0x786500000x000000AB12340000(在64位操作系統上運行32位進程),它可能指向只存在於頁面文件中的內容,或者甚至可能根本不指向任何內容。

當你想分配一個連續地址的內存塊時,它就在這個虛擬地址空間中,這個RAM需要是連續的。對於32位進程,您只能獲得2GB或3GB的可用地址空間,因此儘管存在可用的物理RAM和足夠的空間,但沒有足夠大小的連續塊使用它並不難總未使用的虛擬地址空間。

3

這可能是由內存碎片造成的。

大物體進入大物體堆,它們不會移動以騰出空間放置物體。如果您在可用內存中存在空隙,則這可能導致碎片,當您嘗試分配大於任何可用內存塊的對象時,這可能會導致內存不足。

See here for more details

大於85,000字節的任何對象都將放置在大對象堆上,但閾值僅爲1000倍(或8000字節)的雙精度數組除外。

另請注意,32位.Net程序每個對象的最大限制爲2GB,總體上略低於4GB(可能低於3GB,具體取決於操作系統)。

0

您不應該使用BinaryWriter將文本寫入文件。改爲使用TextWriter

現在使用的是:

for (int i = 0; i < Int32.MaxValue; i++)

這將寫每個寫入(數字表示與換行)至少3個字節。在Int32.MaxValue之前,你至少需要6GB內存才能看到你正在寫入MemoryStream

進一步看你的代碼,你將以任何方式將MemoryStream寫入文件。因此,你可以簡單地做到以下幾點:

for (int i = 0; i < int.MaxValue; i++) 
{ 
    File.AppendAllText("filename.log", i.ToString() + Environment.Newline); 
} 

或寫入到一個開放的TextWriter

TextWriter writer = File.AppendText("filename.log"); 

for (int i = 0; i < int.MaxValue; i++) 
{ 
    writer.WriteLine(i); 
} 

如果你想要一些內存緩衝區,IMO是記錄,你將失去最後一個壞主意在碰撞過程中的寫入位,你可以使用以下的創建TextWriter

StreamWriter(string path, bool append, Encoding encoding, int bufferSize) 

,並通過了「較大的」號碼bufferSize。默認值是1024

要回答這個問題,由於MemoryStream調整大小,並且在某些時候它會變得很大以適應內存(在另一個答案中討論過),所以會出現內存不足異常。

+0

不完全是一個記錄問題,我知道Logger.Net等,但這種情況只是一個簡單的日誌,我也需要知道多少性能會提高(意味着會花多少時間)如果我第一次寫入內存然後寫入文件。 – Razort4x 2013-03-28 11:25:07

相關問題