2012-10-02 13 views
5

Ho alcuni file molto grandi ciascuno della dimensione di 500MB++, contenenti valori integer (in realtà è un po 'più complesso), sto leggendo quei file in un ciclo e calcolando il valore massimo per tutti i files. Per qualche motivo la memoria cresce costantemente durante l'elaborazione, sembra che GC non rilasci mai la memoria, acquisita dalle precedenti istanze di lines.Perdita di memoria (?) Con StreamReader

Non riesco a trasmettere i dati e devo usare GetFileLines per ogni file. A condizione che la quantità effettiva di memoria richiesta per memorizzare lines per un file sia 500MB, perché ottengo 5GB di RAM utilizzato dopo l'elaborazione di 10 file? Alla fine si blocca con l'eccezione di memoria esaurita dopo 15 file.

Calcolo:

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))); 
    } 

codice 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

chiamate il metodo 'lines.Clear()'? – opewix

+0

Io non dovrei? – user1514042

+0

10 x 500 Mb = 5 GB. Si stanno mantenendo tutti i contenuti dei file nell'elenco * Elenco linee *. Stesso perfetto di ReadAllLines() – Cybermaxs

risposta

5

Per file molto grande, metodo ReadLines sarebbe la soluzione migliore, perché è differita esecuzione, non carica tutte le linee in memoria e semplice da usare:

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

Maggiori informazioni:

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

Edit:

Questo è il modo ReadLines attuare dietro le quinte:

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

Inoltre, si consiglia di utilizzare l'elaborazione parallela per migliorare le prestazioni quando si dispone di più file

+0

Scusa se la logica di elaborazione è più complessa, il campione mostra solo il fatto che è completamente disaccoppiato dal processo di lettura. – user1514042

+0

@ user1514042: non importa molto, puoi usarlo con LINQ per elaborare file molto grandi –

+0

Non ucciderà le prestazioni quando il numero effettivo di righe è 500K ++? – user1514042

4

Si potrebbe schiantarsi perché si stanno mantenendo i riferimenti al risultato analizzato in memoria dopo aver terminato il loro trattamento (il codice che mostrano doesn Fallo, ma lo stesso codice che usi?). È altamente improbabile che ci sia un tale errore in StreamReader.

Sei sicuro di aver letto tutto il file in memoria in una volta? Potrebbe essere possibile utilizzare una sequenza di righe enumerabile come IEnumerable<string> invece di caricare uno List<string> in primo piano. Non c'è nulla che vieti questo, almeno in questo codice.

Infine, le chiamate Close e Dispose sono ridondanti; using si occupa automaticamente di questo.

+0

Beh, io uso solo i tipi di valore, possono ancora contenere il riferimento? – user1514042

+0

Certo che possono. Se riesci ad accedere in qualche modo all'elenco, qualcuno sta tenendo un riferimento ad esso. – Jon

+0

Vero, ma viene sostituito ogni volta, il tuo punto sarebbe giusto se fossi infelice finale 500 MB non essere cancellato, ma ho un altro problema. – user1514042

0

Si sta leggendo l'intero file in memmory (linee List)

Credo che si potrebbe semplicemente leggere una riga alla volta e mantenere il numero più alto?

ti farà risparmiare un sacco di RAM.

+0

Ogni riga impiega .5 secondi per l'elaborazione, ecco perché è molto più veloce leggerli e quindi elaborarli. Guadagniamo molto facendo ciò, il che è confermato dai test delle prestazioni. – user1514042

1

Perché non attuare tale come segue:

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

Sembra che stai sempre caricando l'intero file nella memoria.Allo stesso tempo, crei anche oggetti gestiti (Elenco) per ogni riga del file.

Non c'è motivo per cui l'utilizzo della memoria aumenti.

Si prega di inviare resto del codice anche, dubito che si sta da qualche parte avendo riferimento a questo elenco che è in uso e quindi non è stato smaltito.

0

OK, se si desidera una soluzione in cui è possibile leggere l'intero file in una volta, perché si è sicuro che è necessario il guadagno di prestazioni, quindi facciamolo in questo modo in modo che non hai un problema di memoria.

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; 
}