2015-05-27 17 views
10

Desidero formattare e scrivere il contenuto di un grande numero Map (1.785.530 voci) in un file di testo. Dopo che circa l'85% delle voci è stato elaborato, diventa molto lento e quindi ricevo un OutOfMemoryException.FileWriter memoria esaurita

Questo stesso errore si verifica, anche se io:

  • chiamare periodicamente flush() o close() sul mio FileWriter
  • uso BufferedWriter
  • scrivere ogni riga per un StringBuffer prima di scrivere il file

Questo è il mio codice:

private static final TreeMap<Date, Integer> accessesPerSecondMap = new  
    TreeMap<>(); 

... 

private static void writeOutputFile() throws IOException { 
    FileWriter writer = new FileWriter(FILENAME_OUTPUT); 

    writer.write("Date"); 
    writer.write(','); 
    writer.write("Request Count"); 
    writer.write('\n'); 

    for (Date date : accessesPerSecondMap.keySet()) { 

     // first and last date are not precise so do not write it in the 
     // file 
     if (date == accessesPerSecondMap.firstKey() 
       || date == accessesPerSecondMap.lastKey()) { 
      continue; 
     } 

     writer.write(String.valueOf(date)); 
     System.out.println("FileMerger wrote: " + String.valueOf(date)); 
     writer.write(','); 
     writer.write(String.valueOf(accessesPerSecondMap.get(date))); 
     writer.write('\n'); 

    } 

    writer.flush(); 
    writer.close(); 
} 

E qui è l'eccezione che viene generata:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
    at sun.util.resources.TimeZoneNames.getContents(Unknown Source) 
    at sun.util.resources.OpenListResourceBundle.loadLookup(Unknown Source) 
    at sun.util.resources.OpenListResourceBundle.loadLookupTablesIfNecessary(Unknown Source) 
    at sun.util.resources.OpenListResourceBundle.handleKeySet(Unknown Source) 
    at java.util.ResourceBundle.containsKey(Unknown Source) 
    at sun.util.locale.provider.LocaleResources.getTimeZoneNames(Unknown Source) 
    at sun.util.locale.provider.TimeZoneNameProviderImpl.getDisplayNameArray(Unknown Source) 
    at sun.util.locale.provider.TimeZoneNameProviderImpl.getDisplayName(Unknown Source) 
    at sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter.getName(Unknown Source) 
    at sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter.getObject(Unknown Source) 
    at sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter.getObject(Unknown Source) 
    at sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObjectImpl(Unknown Source) 
    at sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObject(Unknown Source) 
    at sun.util.locale.provider.TimeZoneNameUtility.retrieveDisplayName(Unknown Source) 
    at java.util.TimeZone.getDisplayName(Unknown Source) 
    at java.util.Date.toString(Unknown Source) 
    at java.lang.String.valueOf(Unknown Source) 
    at FileMerger.writeOutputFile(FileMerger.java:95) 
    at FileMerger.main(FileMerger.java:26) 

Aumentare lo spazio di heap funziona per me, ma non è una soluzione davvero soddisfacente a mio parere. Tra qualche giorno dovrò scrivere file due volte più grandi; spero che lo spazio sia abbastanza anche per loro.

Sto eseguendo Java 1.8.0_45 su Windows. Il codice sopra è il codice reale e l'unico codice in esecuzione. A scopo di verifica che uso il seguente codice per il riempimento del TreeMap:

int accesses = 43267; 

for (int i = 0; i < 1785530; i++) { 
    Date date = new Date(i); 
    accessesPerSecondMap.put(date, accesses); 
} 

ho aggiunto quanto segue al ciclo for per monitorare l'utilizzo della memoria:

... 
    int count = 100000; 
    for (Date date : accessesPerSecondMap.keySet()) { 

     if (count == 100000) { 
      count = 0; 
      printOutMemoryUsage(); 
     } 
     count++; 
... 

private void printOutMemoryUsage() { 
    Runtime runtime = Runtime.getRuntime(); 

    NumberFormat format = NumberFormat.getInstance(); 

    StringBuilder sb = new StringBuilder(); 
    long maxMemory = runtime.maxMemory(); 
    long allocatedMemory = runtime.totalMemory(); 
    long freeMemory = runtime.freeMemory(); 

    sb.append("free memory: " + format.format(freeMemory/1024) + " "); 
    sb.append("allocated memory: " + format.format(allocatedMemory/1024) 
      + " "); 
    sb.append("max memory: " + format.format(maxMemory/1024) + " "); 
    sb.append("total free memory: " 
      + format.format((freeMemory + (maxMemory - allocatedMemory))/1024) 
      + "<br/>"); 
    System.out.println(sb); 
} 

sto ottenendo questa uscita quando non aumentare lo spazio di heap:

free memory: 28.868 allocated memory: 156.792 max memory: 253.440 total free memory: 125.516 
free memory: 89.847 allocated memory: 253.440 max memory: 253.440 total free memory: 89.847 
free memory: 87.796 allocated memory: 253.440 max memory: 253.440 total free memory: 87.796 
free memory: 89.758 allocated memory: 253.440 max memory: 253.440 total free memory: 89.758 
free memory: 32.478 allocated memory: 253.440 max memory: 253.440 total free memory: 32.478 
free memory: 35.182 allocated memory: 253.440 max memory: 253.440 total free memory: 35.182 
free memory: 37.269 allocated memory: 253.440 max memory: 253.440 total free memory: 37.269 
free memory: 45.165 allocated memory: 253.440 max memory: 253.440 total free memory: 45.165 
free memory: 42.943 allocated memory: 253.440 max memory: 253.440 total free memory: 42.943 
free memory: 32.055 allocated memory: 253.440 max memory: 253.440 total free memory: 32.055 
free memory: 13.053 allocated memory: 253.440 max memory: 253.440 total free memory: 13.053 
free memory: 14.281 allocated memory: 253.440 max memory: 253.440 total free memory: 14.281 
free memory: 12.797 allocated memory: 253.440 max memory: 253.440 total free memory: 12.797 
free memory: 1.973 allocated memory: 253.440 max memory: 253.440 total free memory: 1.973 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 

Con maggiore heapspace -Xms512m -Xmx1230m ottengo:

01.235.
free memory: 372.630 allocated memory: 506.816 max memory: 1.217.536 total free memory: 1.083.350 
free memory: 329.031 allocated memory: 506.816 max memory: 1.217.536 total free memory: 1.039.751 
free memory: 273.121 allocated memory: 506.816 max memory: 1.217.536 total free memory: 983.841 
free memory: 333.700 allocated memory: 506.816 max memory: 1.217.536 total free memory: 1.044.420 
free memory: 276.392 allocated memory: 506.816 max memory: 1.217.536 total free memory: 987.112 
free memory: 220.482 allocated memory: 506.816 max memory: 1.217.536 total free memory: 931.202 
free memory: 279.896 allocated memory: 506.816 max memory: 1.217.536 total free memory: 990.616 
free memory: 223.986 allocated memory: 506.816 max memory: 1.217.536 total free memory: 934.706 
free memory: 284.565 allocated memory: 506.816 max memory: 1.217.536 total free memory: 995.285 
free memory: 228.654 allocated memory: 506.816 max memory: 1.217.536 total free memory: 939.374 
free memory: 169.949 allocated memory: 506.816 max memory: 1.217.536 total free memory: 880.669 
free memory: 230.528 allocated memory: 506.816 max memory: 1.217.536 total free memory: 941.248 
free memory: 174.618 allocated memory: 506.816 max memory: 1.217.536 total free memory: 885.338 
free memory: 235.197 allocated memory: 506.816 max memory: 1.217.536 total free memory: 945.917 
free memory: 179.287 allocated memory: 506.816 max memory: 1.217.536 total free memory: 890.007 
free memory: 123.376 allocated memory: 506.816 max memory: 1.217.536 total free memory: 834.096 
free memory: 183.956 allocated memory: 506.816 max memory: 1.217.536 total free memory: 894.676 
free memory: 128.046 allocated memory: 506.816 max memory: 1.217.536 total free memory: 838.766 
FileMerger main method finished 
+2

È possibile aumentare molto lo spazio di heap da Eclipse molto facilmente –

+3

Hai provato a lavare periodicamente? – Necreaux

+2

Puoi provare a risolvere questo problema aumentando le dimensioni di heap predefinite di Java, ma potresti comunque ottenere questo errore. Quindi dovresti provare a implementare il tuo programma in modo tale da prevenire perdite di memoria e non generare OutOfMemoryError. – Bikku

risposta

3

in Eclipse ... sulla barra degli strumenti click Run->Run Configuration trovare il nome della classe siete stati in esecuzione, selezionarlo, fare clic sul Arguments tab quindi aggiungere:

-Xms512M -Xmx1524M // This may be XS512M or Xx1524M. 

alla sezione Argomenti. Ciò consentirà ai tuoi file più grandi di funzionare in modo più fluido con lo spazio heap più grande come @AliGajani menzionato nei commenti.

+0

poi ho ricevuto _Errore durante l'inizializzazione di VM Impossibile riservare spazio sufficiente per 1560576KB object heap_ – userSJ

+1

Suggerisco di combinare la mia risposta e la risposta di @Tomek. Scrivi la massima tolleranza con la nuova dimensione dell'heap e quindi chiudi il writer. Quindi riapri e fallo di nuovo. – Adam

+2

Se il tuo problema si trova davvero nell'area di non avere abbastanza memoria per la JVM, prova una JVM a 64 bit, ti permetterà di usare più di 1,5 GB di memoria che hai qui (se il tuo computer e la tua architettura lo supportano) – Marged

5
  1. Provare a scrivere per esempio i primi 10000 record. Chiudere lo scrittore, quindi crearlo nuovamente e provare ad aggiungere altri 10000 record e così via.
  2. Provare a utilizzare StringBuilder per creare una stringa singola per iterazione di ciclo e richiamare write() una volta in un'iterazione di ciclo.
8

Ci sono generalmente due modi per sbarazzarsi di questo errore

java.lang.OutOfMemoryError: Java heap space 
  1. accettano la JVM di utilizzare più memoria

Con l'argomento -Xmx JVM, è possibile impostare la dimensione dell'heap. Utilizzare java-Xms-Xmx sulla riga di comando.

  1. Usa Eclipse Memory Analyzer analizzatore di memoria Eclipse è uno strumento da Eclipse Foundation per analizzare java heap dump. Aiuta a trovare perdite di classloader e perdite di memoria e aiuta a minimizzare il consumo di memoria. Puoi usare MAT per analizzare il dump dell'heap che trasporta milioni di oggetti e ti aiuta anche ad estrarre il sospetto di perdita di memoria.
+0

Questo patch solo per il problema - [BufferedWriter] (https://stackoverflow.com/questions/12350248/java-difference-traph-filewriter-and-bufferedwriter) è necessario per mantenere l'utilizzo della memoria sotto controllo. –

+0

@ KenY-N potresti spiegare quale sia la relazione tra 'BufferedWriter' e il controllo dell'uso della memoria? Non riesco a trovare nulla nella domanda collegata o altrove. –

3

Provare a inserire writer.flush(); all'interno del ciclo for. In questo modo credo che il tuo buffer sarà libero invece di diventare pesante.

Problemi correlati