2012-10-02 107 views
5

我有幾個非常大的文件,每個500MB++大小,包含整數值(實際上它有點複雜),我在循環中讀取這些文件,並計算最大值全部文件。由於某些原因,在處理過程中內存不斷增長,看起來GC從未釋放內存,由之前的lines實例獲取。內存泄漏(?)與StreamReader

我無法對數據進行流式處理,必須爲每個文件使用GetFileLines。如果爲一個文件存儲lines所需的實際內存量爲500MB,爲什麼在處理10個文件後使用5GBRAM?最終它會在15個文件後出現內存不足異常。

計算:

int max = int.MinValue; 

    for (int i = 0; i < 10; i++) 
    { 
     IEnumerable<string> lines = Db.GetFileLines(i); 

     max = Math.Max(max, lines.Max(t=>int.Parse(t))); 
    } 

GetFileLines代碼:

public static List<string> GetFileLines(int i) 
    { 
     string path = GetPath(i); 

     // 
     List<string> lines = new List<string>(); 
     string line; 

     using (StreamReader reader = File.OpenText(path)) 
     { 
     while ((line = reader.ReadLine()) != null) 
     { 
      lines.Add(line); 
     } 

     reader.Close(); 
     reader.Dispose(); // should I bother? 
     } 

     return lines; 
    } 
+0

你叫'lines.Clear()'方法嗎? – opewix

+0

我不'應該嗎? – user1514042

+0

10 x 500 Mb = 5 GB。您將所有文件內容保存在列表中*列表行*。與ReadAllLines()相同的性能() – Cybermaxs

回答

5

對於非常大的文件,方法ReadLines將是最好的選擇,因爲它是延遲執行,它不會加載所有線路在內存和簡單使用方法:

Math.Max(max, File.ReadLines(path).Max(line => int.Parse(line))); 

的更多信息:

http://msdn.microsoft.com/en-us/library/dd383503.aspx

編輯:

這是ReadLines如何實現幕後:

public static IEnumerable<string> ReadLines(string fileName) 
    { 
     string line; 
     using (var reader = File.OpenText(fileName)) 
     { 
      while ((line = reader.ReadLine()) != null) 
       yield return line; 
     } 
    } 

而且,它是使用並行處理來改善,當你有多個文件

+0

抱歉,處理邏輯更復雜,該示例僅表明它完全與讀取過程分離的事實。 – user1514042

+0

@ user1514042:沒什麼關係,你可以在LINQ中使用它來處理非常大的文件 –

+0

當實際的行數是500K ++時它不會殺死性能嗎? – user1514042

4

你可能會崩潰,因爲你遵守瞭解析結果的引用在內存中完成後處理它們之後(你的代碼顯示沒有按這樣做,但是你運行相同的代碼?)。在StreamReader中有這樣的錯誤是不太可能的。

您確定您必須一次讀取內存中的所有文件嗎?可能很有可能使用可排序的行序列作爲IEnumerable<string>,而不是預先加載List<string>。至少在這個代碼中沒有任何東西禁止這個。

最後,CloseDispose調用是多餘的; using自動處理。

+0

那麼我只使用值類型,他們仍然可以保存參考? – user1514042

+0

當然可以。如果您可以通過某種方式訪問​​該列表,則有人持有對該列表的引用。 – Jon

+0

確實如此,但是它被替換爲everythime,如果我最後的500MB不被清除,你的觀點是正確的,但是我有一個不同的問題。 – user1514042

0

您正在閱讀的整個文件到memmory(列表行)

我想你可能只是一次讀取一行,並保持最高的號碼是多少?

它會爲您節省很多內存。

+0

每一行需要0.5秒的時間來處理,這就是爲什麼閱讀起來然後處理更快的時間。我們獲得了很多不錯的成績,這通過性能測試得到證實。 – user1514042

1

爲什麼沒有實現這些如下:

int max = Int32.MinValue; 
using(var reader = File.OpenText(path)) 
{ 
    while ((line = reader.ReadLine()) != null) 
    { 
     int current; 
     if (Int32.TryParse(line, out current)) 
      max = Math.Max(max, current); 
    }  
} 
0

看起來性能推薦你總是在內存中加載整個文件。同時,您還爲文件的每一行創建託管對象(列表)。

沒有理由說你的內存使用量會增長。

請發佈其他代碼,我懷疑你是否在某處引用了正在使用的這個列表,因此它沒有被處置。

0

好吧,如果你想一個解決方案,您可以讀取一次整個文件,因爲你肯定,你需要的是性能增益,然後讓我們做這樣的,這樣的話你就沒有內存問題。

public static int GetMaxForFile(int i) 
{ 
    string path = GetPath(i); 

    var lines = new List<string>(File.ReadAllLines(path)); 

    // you MUST perform all of your processing here ... you have to let go 
    // of the List<string> variable ... 
    int max = Math.Max(max, lines.Max(t=>int.Parse(t))); 

    // this may be redundant, but it will cause GC to clean up immediately 
    lines.Clear(); 
    lines = null; 

    return max; 
}