Errore java.lang.OutOfMemoryError: Superato il limite di overhead del GC

Ricevo questo messaggio di errore quando eseguo i miei test JUnit:

java.lang.OutOfMemoryError: GC overhead limit exceeded

So cos'è un OutOfMemoryError, ma cosa significa GC overhead limit? Come posso risolvere questo problema?

Soluzione

Questo messaggio significa che per qualche motivo il garbage collector sta impiegando una quantità eccessiva di tempo (di default il 98% di tutto il tempo della CPU del processo) e recupera poca memoria in ogni esecuzione (di default il 2% dell'heap).

Questo significa effettivamente che il vostro programma smette di fare qualsiasi progresso ed è occupato a eseguire solo la garbage collection per tutto il tempo.

Per evitare che la vostra applicazione assorba il tempo della CPU senza fare nulla, la JVM lancia questo Errore in modo che abbiate la possibilità di diagnosticare il problema.

I rari casi in cui ho visto accadere questo è dove qualche codice stava creando tonnellate di oggetti temporanei e tonnellate di oggetti debolmente referenziati in un ambiente già molto limitato in termini di memoria.

Controllate questo articolo per i dettagli (in particolare questa parte).

Commentari (14)

Il GC lancia questa eccezione quando viene speso troppo tempo nella garbage collection per un ritorno troppo basso, ad esempio il 98% del tempo della CPU viene speso nel GC e meno del 2% dell'heap viene recuperato.

Questa caratteristica è progettata per evitare che le applicazioni vengano eseguite per un lungo periodo di tempo mentre fanno poco o nessun progresso perché l'heap è troppo piccolo.

Puoi disattivarla con l'opzione della linea di comando -XX:-UseGCOverheadLimit

Maggiori informazioni qui

EDIT: sembra che qualcuno possa scrivere più velocemente di me :)

Commentari (6)

Di solito è il codice. Ecco un semplice esempio:

import java.util.*;

public class GarbageCollector {

    public static void main(String... args) {

        System.out.printf("Testing...%n");
        List list = new ArrayList();
        for (int outer = 0; outer < 10000; outer++) {

            // list = new ArrayList(10000); // BAD
            // list = new ArrayList(); // WORSE
            list.clear(); // BETTER

            for (int inner = 0; inner < 10000; inner++) {
                list.add(Math.random());
            }

            if (outer % 1000 == 0) {
                System.out.printf("Outer loop at %d%n", outer);
            }

        }
        System.out.printf("Done.%n");
    }
}

Usando java 1.6.0_24-b07 su un Windows7 32 bit.

java -Xloggc:gc.log GarbageCollector

Poi guarda gc.log

  • Attivato 444 volte usando il metodo BAD
  • Attivato 666 volte usando il metodo PEGGIORE
  • Attivato 354 volte usando il metodo MIGLIORE

Ora, garantito, questo non è il miglior test o il miglior design, ma quando ci si trova di fronte a una situazione in cui non si ha altra scelta che implementare un tale ciclo o quando si ha a che fare con codice esistente che si comporta male, scegliere di riutilizzare gli oggetti invece di crearne di nuovi può ridurre il numero di volte che il garbage collector si mette in mezzo...

Commentari (1)