2012-11-25 46 views
-1

我正在使用TCP通過網絡發送/接收文件的程序中工作。 程序發送多個文件,因此在用戶退出程序之前,流不會關閉。發送文件時C#內存使用量巨大,System.OutOfMemoryException

我面對的問題是,當我發送一個700MB文件時,我的服務器程序私有內存增長到700,000 K,嚴重影響了我的計算機性能。當試圖發送另一個700MB文件時,服務器會拋出一個System.OutOfMemoryException

有人可以告訴我我做錯了什麼,或不做?

服務器端代碼:

using ( FileStream fs = new FileStream("dracula.avi", FileMode.Open, FileAccess.Read))        
    {               
    byte[] data = new byte[fs.Length]; 
    int remaining = data.Length; 
    int offset = 0; 

    strWriter.WriteLine("Content-Length: " + data.Length); 
    strWriter.Flush(); 

    Thread.Sleep(1000); 
    while (remaining > 0) 
     { 
      Thread.Sleep(10); 
      int read = fs.Read(data, offset, remaining); 
      remaining -= read; 
      offset += read; 

     } 

    fs.Flush(); 
    fs.Close(); 
} 

    strm.Write(data, 0, data.Length); 
    strm.Flush(); 
    GC.Collect(); 
+0

不要聲明像byte [] data = new byte [fs.Length];'這樣的數組。在較小的卡盤中讀取數據並將其發送到循環中 –

+0

謝謝,這就是我要做的。 – Spreadzz

回答

4

你目前正在讀整個文件到內存中,即使你只想把它複製到另一個流。不要這樣做。每次迭代一個塊:讀取塊,寫入塊,讀取塊,寫入塊等。如果您使用的是.NET 4,則可以使用Stream.CopyTo作爲此目的。

+0

我也認爲我的工作,但我認爲我的蜜蜂有一種方法來釋放內存。再次感謝你,喬恩。 – Spreadzz

+0

是否有可以讀取和寫入的安全字節數?我目前使用4096,它接縫非常慢? – Spreadzz

+0

@Spreadzz:我會親自使用32K而不是4K,但即使使用4K,它也不應該「非常慢」。你應該發佈你試過的代碼(編輯你的問題)。另外,如果你仍然有'Thread.Sleep'調用(目前尚不清楚爲什麼會有這個開始),這顯然是一個壞主意...... –

3

你正在緩衝你的讀取,但不是你的寫入。該程序完全按照您的要求進行操作 - 在發送單個字節之前分配一塊巨大的塊或內存並填充它。

一個更好的方法是從文件中讀取一個小塊(爲了參數,4096字節),然後將塊寫入輸出流。通過這樣做,您將只使用每個連接4096個字節,這是可擴展的。

+0

是否有安全數量的字節,我讀和寫?它像閱讀和寫作4096非常緩慢。 – Spreadzz

1

只要您閱讀它們,最好立即發送數據塊。我沒有測試代碼,但它應該類似於類似的東西;

var bufferLenght = 1024; 
    byte[] buffer; 

    while (remaining > 0) 
    { 
      buffer = new byte[1024]; 
      int len = fs.Read(buffer, offset, bufferLenght); 
      remaining -= len; 
      offset += len;  
      strm.Write(buffer, 0, len); 
    } 
+0

這不適用於此代碼。 – Spreadzz

+0

我沒有測試代碼,但它應該是類似的。 – HuseyinUslu

1

當您用完系統內存或32位進程超出地址空間(2000MB)時,通常會出現OOM情況。

你說它可以成功複製一個而不是兩個?這兩個是同時還是連續?你的線程模型是什麼?另外,這個例子是一個片段,你似乎有一個StreamWriter和一個用於寫作的Stream,這些對象是否會消失?

請注意GC.Collect。微軟不建議顯式調用,因爲如果你沒有正確使用它,它可能會導致對象的運行時間超過所需的時間。這是因爲當你做一個GC.Collect時,你正在向更高一代推廣對象。根據我的經驗,最好確保你釋放對象並讓框架決定什麼時候GC.Collect。

我會熟悉WinDBG + SOS,這可以讓你看看堆上的對象。

試試這個:

  1. 啓動WinDBG的,並連接到您的過程
  2. 類型 「.loadby SOS CLR」 如果使用的4.0,否則輸入 「.loadby索斯mscorwks」
  3. 按F5繼續
  4. 複製一個文件,等待它完成
  5. 按CTRL + BREAK
  6. 鍵入「!dumpheap -stat「,查看結果,查找應該丟失的對象
  7. For-Each應該消失的對象,抓取MT值
  8. 鍵入」!dumpheap -mt {0}「替換{0}從上述步驟
  9. 值。這是實例的列表,搶的對象之一地址
  10. 類型「!gcroot {0}」替換{0}對象解決

這應該告訴你是什​​麼使根對象成爲根,然後你需要找出如何取消根,例如不需要的空對象。

+0

我連續嘗試兩個文件。這是因爲我在byte []中加載了整個流而不是小塊。我修復它。但也謝謝你,我一定會給WinDBG一個嘗試。 – Spreadzz

相關問題