2012-08-30 7 views
15

Ho un grande file di testo con 20 milioni di righe di testo. Quando leggo il file utilizzando il seguente programma, funziona perfettamente e in effetti riesco a leggere file molto più grandi senza problemi di memoria.Il mio programma Java che legge un grande file di testo sta esaurendo la memoria, qualcuno può aiutare a spiegare perché?

public static void main(String[] args) throws IOException { 
    File tempFile = new File("temp.dat"); 
    String tempLine = null; 
    BufferedReader br = null; 
    int lineCount = 0; 
    try { 
     br = new BufferedReader(new FileReader(tempFile)); 
     while ((tempLine = br.readLine()) != null) { 
      lineCount += 1; 
     } 
    } catch (Exception e) { 
     System.out.println("br error: " +e.getMessage()); 
    } finally { 
     br.close(); 
     System.out.println(lineCount + " lines read from file"); 
    } 
} 

Tuttavia, se ho bisogno di aggiungere alcuni record di questo file prima di leggerlo, il BufferedReader consuma una grande quantità di memoria (ho appena usato task manager di Windows per monitorare questo, non molto scientifico lo so, ma dimostra il problema). Il programma modificato è il seguente, che è lo stesso del primo, ad eccezione del fatto che accludo un singolo record al file per primo.

public static void main(String[] args) throws IOException { 
    File tempFile = new File("temp.dat"); 
    PrintWriter pw = null; 
    try { 
     pw = new PrintWriter(new BufferedWriter(new FileWriter(tempFile, true))); 
     pw.println(" "); 
    } catch (Exception e) { 
     System.out.println("pw error: " + e.getMessage()); 
    } finally { 
     pw.close(); 
    } 

    String tempLine = null; 
    BufferedReader br = null; 
    int lineCount = 0; 
    try { 
     br = new BufferedReader(new FileReader(tempFile)); 
     while ((tempLine = br.readLine()) != null) { 
      lineCount += 1; 
     } 
    } catch (Exception e) { 
     System.out.println("br error: " +e.getMessage()); 
    } finally { 
     br.close(); 
     System.out.println(lineCount + " lines read from file"); 
    } 
} 

Una schermata Windows task manager, in cui la grande urto nella riga mostra il consumo di memoria quando si esegue la seconda versione del programma.

task manager screenshot

quindi ero in grado di leggere questo file, senza esaurire la memoria. Ma ho file molto più grandi con oltre 50 milioni di record, che incontrano un'eccezione di memoria insufficiente quando eseguo questo programma contro di loro? Qualcuno può spiegare perché la prima versione del programma funziona bene su file di qualsiasi dimensione, ma il secondo programma si comporta in modo così diverso e finisce in errore? Sono in esecuzione su Windows 7 con:

versione java "1.7.0_05"
Java (TM) SE Runtime Environment (build 1.7.0_05-b05)
Java HotSpot (TM) Cliente VM (build 23.1-B03 , modalità mista, condivisione)

+1

E 'la 'BufferedReader' che prende tutta la memoria? Preferirei sospettare che sarebbe il "FileWriter" a farlo. –

+1

C'è un motivo per aggiungere un 'BufferedWriter' nel mix? Hai ancora lo stesso problema se esegui 'new PrintWriter (new FileWriter (...))'? –

+2

(Niente a che vedere con la domanda, ma devo sottolineare che è possibile ottenere un NPE nel blocco finally. Il modo per gestirlo è utilizzare la risorsa try-with di Java SE 7 o utilizzare Java SE 6 prova separata per l'ultimo e cattura ed evita l'uso di null.) –

risposta

-3

Avrete bisogno di avviare java con un heap più grande. Prova -Xmx1024m come parametro sul comando java.

Fondamentalmente avrete bisogno di più memoria rispetto alla dimensione del file.

+6

Puoi spiegare perché ho bisogno un mucchio più grande per il secondo programma ma non il primo? La prima versione del programma funziona perfettamente e utilizza una dimensione heap molto piccola. BufferedReader elabora la riga del file 1 alla volta, quindi non dovrebbe avere bisogno di molta memoria? –

+0

Sono d'accordo con tony_h. –

0

Ogni volta che si esegue il java seguente routine di Java, si sta creando un nuovo oggetto:

tempLine = br.readLine() 

Credo che ogni volta che si chiama readLine() è probabilmente la creazione di un nuovo oggetto String che viene lasciato sul l'heap ogni volta che viene chiamata la riassegnazione per assegnare il valore a tempLine.

Pertanto, poiché GC non viene chiamato costantemente, migliaia di oggetti possono essere lasciati nell'heap in pochi secondi.

Alcune persone dicono che sia una cattiva idea chiamare System.gc() ogni 1000 righe circa, ma sarei curioso di sapere se questo risolve il problema. Inoltre, è possibile eseguire questo comando dopo ogni riga di fondo contrassegnare ogni oggetto come spazzatura da collezione:

tempLine=null; 
+0

Non penso sia questo il problema. Quando eseguo la versione readonly del programma, BufferedReader funziona perfettamente senza problemi di memoria. Il problema si verifica solo quando precedo la lettura del file con una sezione che aggiunge una riga al file usando un printwriter. –

+0

Qual è il numero di linee nell'eccezione? Inoltre, se si usa JDK 1.6.0_22 o superiore, credo che si ottenga un garbage collector multithread e sono curioso di sapere quale comportamento si ottiene con quello? Inoltre, BufferedWriter non ti permette di aumentare la dimensione del buffer? Alternativa: provare a utilizzare InputStreamReader e FileInputStream per leggere e quindi archiviare i dati in un char, quindi scrivere quel carattere utilizzando un FileOutputStream. – djangofan

0
 pw = new PrintWriter(new BufferedWriter(new FileWriter(tempFile, true))); 

non sei provare a utilizzare un BufferedWriter? Se aggiungi qualche riga alla fine forse non hai bisogno di un buffer? Se lo fai, prendi in considerazione l'uso di un array di byte (collezioni o generatore di stringhe). Finalmente hai provato la stessa cosa con java 1.6_32? Potrebbe essere un bug nella nuova versione di uno degli Scrittori.

È possibile stampare la memoria libera dopo prima e dopo pw.close(); ?

System.out.println("before wr close :" + Runtime.getRuntime().freeMemory()); 

e simili dopo la chiusura e dopo il lettore vicino

0

Potrebbe essere perché non si può essere aver avanzamento riga/ritorno a capo nel file a tutti. In questo caso, readLine() tenta di creare una sola stringa dal tuo file che probabilmente sta esaurendo la memoria.

Java doc di readLine():

Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed.

+0

Purtroppo non è questo il problema, i file sono tutti delineati in modo corretto e sto ottenendo i conteggi di linea corretti mentre analizzo i file. –

0

Hai provato:

A) la creazione di una nuova istanza di file da utilizzare per la lettura, ma che punta allo stesso file. e B) leggendo un file completamente diverso nella seconda parte.

Mi chiedo se entrambi, l'oggetto File è ancora in qualche modo collegato a PrintWriter o se il sistema operativo sta facendo qualcosa di divertente con gli handle di file. Questi test dovrebbero mostrarti dove concentrarti.

Questo non sembra essere un problema con il codice, e la tua logica per pensare che non dovrebbe rompere sembra valida, quindi deve essere una funzionalità di base.

+0

Grazie a @Glen Lamb, penso che i tuoi suggerimenti abbiano molto senso. Tuttavia, avevo già dedicato troppo tempo a questo problema e alla fine ho deciso di farlo in un altro modo per evitare del tutto questo problema.Se mai avrò tempo per tornare ad esso, posterò qualsiasi risultato che otterrò. –

1

è possibile avviare una Java VM con VM-Options

-XX:+HeapDumpOnOutOfMemoryError 

questo scriverà un heap dump in un file, che possono essere analizzati per la ricerca di sospetti di fughe

Usa un '+' per aggiungere un opzione e un '-' per rimuovere un'opzione.

Se si utilizza Eclipse Memory Analyzer plugin Java MAT per ottenere Mucchio-Dump da VM in esecuzione con alcuni bei analisi per fughe sospetti ecc

Problemi correlati