Come forzare la garbage collection in Java?


225

È possibile forzare la garbage collection in Java, anche se è difficile da fare? Conosco System.gc();e Runtime.gc();suggeriscono solo di fare GC. Come posso forzare GC?


30
Forse sarebbe utile fornire alcune informazioni sul perché è necessario forzare GC. In genere in un linguaggio garbage collection è una cattiva pratica chiamare esplicitamente il raccoglitore.
Justin Ethier,

3
Una data JVM può fornire diversi metodi di raccolta dei rifiuti, ognuno con i suoi vantaggi e svantaggi, e spesso una determinata situazione può essere evitata semplicemente suggerendo la JVM al momento dell'avvio. Si prega di approfondire lo scenario.
Thorbjørn Ravn Andersen,

3
-histo jmap: live <pid> stackoverflow.com/questions/6418089/...

5
Ecco un caso d'uso per forzare la garbage collection: ho un server con un heap da 30 GB, di cui ~ 12 GB vengono generalmente utilizzati (oggetti ~ 5 M). Ogni 5 minuti, il server impiega circa un minuto per eseguire un'attività complessa in cui vengono utilizzati circa 35 milioni di oggetti aggiuntivi. Un GC completo viene attivato un paio di volte all'ora, invariabilmente durante l'attività complessa, e congela la VM per 10-15 secondi. Mi piacerebbe forzare l'esecuzione dell'intero GC in un momento in cui l'attività complessa non è in esecuzione; sarebbe quindi destreggiarsi tra oggetti live 5M anziché 40M.
Steve,

3
@JustinEthier C'è un caso abbastanza ovvio in cui potresti voler forzare il GC, che è il test unitario di qualsiasi comportamento che coinvolge la gerarchia di tipi java.lang.ref.Reference.
Elias Vasylenko,

Risposte:


168

La tua migliore opzione è quella di chiamare System.gc()che è semplicemente un suggerimento per il garbage collector che vuoi che faccia una raccolta. Non c'è modo di forzare e la raccolta immediata anche se il garbage collector non è deterministico.


28
Ci dovrebbe essere. non-deterministic == trouble
Pacerier,

7
Un garbage collector può essere non deterministico e offrire comunque un modo per forzare una raccolta immediata. Ad esempio, in genere il raccoglitore .NET non è deterministico ma una chiamata a GC.Collect () impone l'esecuzione. È solo che Java sceglie di non esporre questa funzione.
Petr Hudeček,

2
Nella mia esperienza, questo metodo invoca sempre il Garbage Collector. Lo fa con sufficiente regolarità che i miei grafici di memoria utilizzati rispetto al numero di oggetti dichiarati sono sempre rigorosamente lineari (tenendo conto del riempimento, ecc.).
Jim Pivarski,

Stavo pensando che allocando nuovi oggetti e quindi non facendoli più riferimento, il garbage collector funzionerebbe automaticamente
Bionix1441,

@ PetrHudeček Nelle applicazioni del mondo reale .NET GC.Collect()non raccoglie. In Java gc()fa.
ajeh

53

La libreria jlibs ha una buona classe di utilità per la garbage collection . Puoi forzare la garbage collection usando un piccolo trucco con gli oggetti WeakReference .

RuntimeUtil.gc () dai jlibs:

   /**
    * This method guarantees that garbage collection is
    * done unlike <code>{@link System#gc()}</code>
    */
   public static void gc() {
     Object obj = new Object();
     WeakReference ref = new WeakReference<Object>(obj);
     obj = null;
     while(ref.get() != null) {
       System.gc();
     }
   }

1
Questo codice è rotto perché un riferimento debole viene cancellato non appena il suo referente diventa debolmente raggiungibile, che è prima che venga cancellato dalla memoria.
Marko Topolnik,

1
È possibile che si stia fondendo il significato di "GC is run" con "memory is reclared". L'oggetto sopravvive e non è ancora nemmeno finalizzato, ma non è più possibile accedervi tramite il riferimento debole. Un modo un po 'migliore sarebbe usare a PhantomReferencecon a ReferenceQueuee poi verrai avvisato dopo la finalizzazione, ma ancora prima della pulizia. Infine, anche se hai rilevato con successo che la memoria per questo oggetto è stata recuperata, significherebbe molto poco in un GC generazionale come HotSpot. Di solito coinciderebbe con la pulizia delle giovani generazioni.
Marko Topolnik,

20
OP ha richiesto, e tu hai dichiarato di fornire, una soluzione per "forzare la raccolta dei rifiuti". L'esecuzione del sottosistema GC è una cosa, in realtà raccogliere un'altra spazzatura. L'esempio di codice fornito ha chiaramente l'intenzione di garantire che sia stata raccolta la spazzatura. Ad ogni modo, trattandosi di una domanda molto antica, non si tratta ovviamente dei desideri di OP, ma dell'utilità per il grande pubblico. Nessuno è interessato a "forzare l'esecuzione del sottosistema GC" da solo, senza raccolta di rifiuti. In effetti, le persone di solito vogliono avere la garanzia che tutta la spazzatura sia stata raccolta.
Marko Topolnik,

4
Probabilmente non lo hai confrontato con l'efficienza di System.gc(); System.gc();, ma sarebbe sicuramente interessante sapere se ha mai funzionato meglio di così. In effetti, basta stampare quante volte è stato chiamato System.gc(). La possibilità di raggiungere mai 2 è piuttosto scarsa.
Marko Topolnik,

3
@MarkoTopolnik: "Nessuno è interessato a" forzare l'esecuzione del sottosistema GC "da solo, senza raccolta di rifiuti" .... In realtà oggi ero interessato a questo comportamento. Sono grato che questa risposta fosse presente. Il mio scopo era verificare il comportamento rotazionale della gestione dei log del GC e ottenere il formato dell'output del GC. Questo piccolo trucco mi ha aiutato a riempire rapidamente i registri GC.
erik.weathers

49

Il modo migliore (se non solo) per forzare un GC sarebbe quello di scrivere una JVM personalizzata. Credo che i Garbage Collector siano collegabili, quindi probabilmente potresti semplicemente scegliere una delle implementazioni disponibili e modificarla.

Nota: questa NON è una risposta facile.


40
+1 per lulz. nulla rende il debug frustrante migliore di qualcuno con un senso dell'umorismo. diverso da una risposta effettivamente utilizzabile, cioè.
jsh


25

, è quasi possibile costringerti a chiamare metodi nello stesso ordine e allo stesso tempo questi sono:

System.gc ();
System.runFinalization ();

anche se è solo un oggetto per ripulire contemporaneamente l'uso di questi due metodi, forzare il garbage collector a utilizzare il finalise()metodo dell'oggetto non raggiungibile liberando la memoria assegnata e facendo ciò che il finalize()metodo afferma.

TUTTAVIA è una pratica terribile usare il Garbage Collector perché il suo utilizzo potrebbe introdurre un sovraccarico nel software che potrebbe essere anche peggio che in memoria, il Garbage Collector ha il suo thread che non è possibile controllare più a seconda di l'algoritmo usato da gc potrebbe richiedere più tempo ed è considerato molto inefficiente, dovresti controllare il tuo software se è peggio con l'aiuto di gc perché è sicuramente rotto, una buona soluzione non deve dipendere da gc.

NOTA: solo per tenere a mente questo funzionerà solo se nel metodo finalize non è una riassegnazione dell'oggetto, se ciò accade l'oggetto rimarrà in vita e avrà una resurrezione che è tecnicamente possibile.


10
NO , anche questi due comandi NON forzano una garbage collection. Come già accennato da altri, gc()è solo un suggerimento per eseguire una garbage collection. runFinalizers()esegue solo i finalizzatori su oggetti "che sono stati trovati scartati". Se il gc non funzionasse effettivamente, potrebbero non esserci oggetti del genere ...
Steffen Heil

inoltre, System.runFinalization () non è una garanzia che qualcosa verrà eseguito; è possibile che non accada nulla. È un suggerimento - da Javadoc: "La chiamata a questo metodo suggerisce che la Java Virtual Machine impieghi sforzi per eseguire i metodi di finalizzazione degli oggetti che sono stati trovati scartati ma i cui metodi di finalizzazione non sono ancora stati eseguiti "
Kaan

21

Nella documentazione per OutOfMemoryError dichiara che non verrà generato a meno che la VM non abbia recuperato la memoria a seguito di una garbage collection completa. Quindi se continui ad allocare memoria fino a quando non ricevi l'errore, avrai già forzato una garbage collection completa.

Presumibilmente la domanda che volevi davvero porre era "come posso recuperare il ricordo che penso dovrei essere recuperato dalla raccolta dei rifiuti?"


18

Per richiedere manualmente GC (non da System.gc ()):

  1. Vai a: cartella bin in JDK ad es.-C: \ Programmi \ Java \ jdk1.6.0_31 \ bin
  2. Apri jconsole.exe
  3. Connettersi al processo locale desiderato.
  4. Vai alla scheda di memoria e fai clic su Esegui GC.

3
Oppsss fuorviante. Passare il mouse sul pulsante "Esegui GC". Puoi richiedere a JVM di eseguire GC ma non forzare mai.
Kumaran,

@PinkeshSharma, questo non forza . È una semplice richiesta che probabilmente potrebbe essere completamente ignorata.
Pacerier,

@Pacerier In un mondo ideale sì .. ma se lo fai vedrai che c'è un aumento della memoria all'istante ...
Pinkesh Sharma

11

.gc è un candidato all'eliminazione nelle versioni future - un tecnico Sun ha commentato che forse meno di venti persone al mondo sanno effettivamente come usare .gc () - Ho lavorato un po 'ieri sera per alcune ore su un argomento centrale / critico struttura dei dati che utilizza i dati generati da SecureRandom, in qualche posto appena oltre 40.000 oggetti, la VM rallenterebbe come se avesse esaurito i puntatori. Chiaramente si stava soffocando su tabelle di puntatori a 16 bit e presentava un comportamento classico di "macchine difettose".

Ho provato -Xms e così via, ho continuato a girare un po 'fino a quando non sarebbe arrivato a circa 57, xxx qualcosa. Quindi eseguirà gc andando da diciamo 57.127 a 57.128 dopo un gc () - a circa il ritmo di code-bloat al campo Easy Money.

Il tuo progetto ha bisogno di una rielaborazione fondamentale, probabilmente di un approccio a finestra scorrevole.


1
Ho qualcosa del genere, un sacco di oggetti in memoria che non posso deallocarli. Viene generata un'eccezione OutOfMemory, voglio forzare GC a verificare se esiste un processo di creazione di oggetti infiniti o questi oggetti sono quelli utilizzati dal mio sistema.

Sembra che tu stia lavorando allo stesso problema, spiega: "Creazione di oggetti infiniti" ... buon progetto di ricerca, forse puoi pubblicare una domanda o qualcosa in un'area java qui (sono un po 'nuovo qui e non conosco il "Finite Automa" di come funziona il sito) Ho provato ieri e ho finito per fare file.dat dato che il compilatore si è lamentato di "troppo codice" su 40.000 base36 BigIntegers codificato come stringa finale statica [] qui e speculo sul fatto che l'intera JVM è limitata su puntatori a 16 bit, scommetto che ciò che dobbiamo fare è aggressivamente nullo e leggere dal disco ...
Nicholas Jordan

Davvero, non ti capisco. Ma per essere chiari su "Infinite Object Creation" intendevo dire che nel mio grande sistema c'è un pezzo di codice che crea oggetti che gestisce e vive nella memoria, in realtà non ho potuto ottenere questo pezzo di codice, solo un gesto !!

5
Senza senso! C'è un caso ovvio in cui dovrebbe essere usato: testare un codice che utilizza riferimenti deboli, in modo che possiamo assicurarci che il comportamento sia corretto quando i riferimenti deboli vengono chiariti.
Elias Vasylenko,


6

La specifica JVM non dice nulla di specifico sulla garbage collection. Per questo motivo, i fornitori sono liberi di implementare GC a modo loro.

Quindi questa vaghezza provoca incertezza nel comportamento della raccolta dei rifiuti. Dovresti controllare i dettagli della tua JVM per conoscere gli approcci / algoritmi di garbage collection. Inoltre ci sono opzioni per personalizzare anche il comportamento.


4

Se devi forzare la raccolta dei rifiuti, forse dovresti considerare come gestisci le risorse. Stai creando oggetti di grandi dimensioni che persistono nella memoria? Stai creando oggetti di grandi dimensioni (ad es. Classi grafiche) che hanno Disposableun'interfaccia e non chiamano dispose()quando hai finito? Stai dichiarando qualcosa a livello di classe di cui hai bisogno solo all'interno di un singolo metodo?


2

Sarebbe meglio se descrivi il motivo per cui hai bisogno della raccolta dei rifiuti. Se si utilizza SWT, è possibile disporre risorse come Imagee Fontper liberare memoria. Per esempio:

Image img = new Image(Display.getDefault(), 16, 16);
img.dispose();

Ci sono anche strumenti per determinare risorse non poste.


cosa succede se non esiste alcun metodo di smaltimento?
ArifMustafa,

1
Completamente estraneo alla domanda! No, non sto usando SWT. Sto chiamando un metodo JNI che apre una finestra .NET attraverso un livello nativo di Delphi. Ho anche un core di calcolo FORTRAN che riceve i dati attraverso un livello C ++ nativo. Che c'entra? Posso forzare un GC o no? No? :-(
Mostafa Zeinali

0

Se stai esaurendo la memoria e ne OutOfMemoryExceptionstai ottenendo una , puoi provare ad aumentare la quantità di spazio heap disponibile per java avviando il programma con java -Xms128m -Xmx512manziché semplicemente java. Questo ti darà una dimensione iniziale dell'heap di 128 Mb e un massimo di 512 Mb, che è molto più di 32 Mb / 128 Mb standard.


Le impostazioni di memoria predefinite sonojava -Xms512M -Xmx1024M
ThePyroEagle il

0

Un'altra opzione è quella di non creare nuovi oggetti.

Il pool di oggetti è assente per ridurre la necessità di GC in Java.

Il pool di oggetti in genere non sarà più veloce della creazione di oggetti (specialmente per oggetti leggeri) ma è più veloce di Garbage Collection. Se hai creato 10.000 oggetti e ogni oggetto era di 16 byte. Sono 160.000 byte che GC deve recuperare. D'altra parte, se non sono necessari tutti i 10.000 contemporaneamente, è possibile creare un pool per riciclare / riutilizzare gli oggetti che elimina la necessità di costruire nuovi oggetti ed elimina la necessità di vecchi oggetti GC.

Qualcosa del genere (non testato). E se vuoi che sia thread-safe, puoi scambiare il LinkedList con un ConcurrentLinkedQueue.

public abstract class Pool<T> {
    private int mApproximateSize;
    private LinkedList<T> mPool = new LinkedList<>();

    public Pool(int approximateSize) {
        mApproximateSize = approximateSize;
    }

    public T attain() {
        T item = mPool.poll();
        if (item == null) {
            item = newInstance();
        }
        return item;
    }

    public void release(T item) {
        int approxSize = mPool.size(); // not guaranteed accurate
        if (approxSize < mApproximateSize) {
            recycle(item);
            mPool.add(item);
        } else if (approxSize > mApproximateSize) {
            decommission(mPool.poll());
        }
    }

    public abstract T newInstance();

    public abstract void recycle(T item);

    public void decommission(T item) { }

}

0

Su OracleJDK 10 con G1 GC, una singola chiamata System.gc()farà sì che GC ripulisca la vecchia raccolta. Non sono sicuro che GC funzioni immediatamente. Tuttavia, GC non pulirà la Young Collection anche se System.gc()viene chiamata più volte in un ciclo. Per ottenere GC per ripulire la Young Collection, è necessario allocare in un ciclo (ad esempio new byte[1024]) senza chiamare System.gc(). Chiamare System.gc()per qualche motivo impedisce a GC di ripulire la Young Collection.


0

Davvero, non ti capisco. Ma per essere chiari su "Infinite Object Creation" intendevo dire che nel mio grande sistema c'è un pezzo di codice che crea oggetti che gestisce e vive nella memoria, in realtà non ho potuto ottenere questo pezzo di codice, solo un gesto !!

Questo è corretto, solo gesto. Hai praticamente le risposte standard già fornite da diversi poster. Prendiamolo uno per uno:

  1. Non ho potuto ottenere questo pezzo di codice in realtà

Esatto, non esiste un vero jvm - tale è solo una specifica, un mucchio di informatica che descrive un comportamento desiderato ... Di recente ho scavato nell'inizializzazione degli oggetti Java dal codice nativo. Per ottenere ciò che vuoi, l'unico modo è fare quello che viene chiamato annullamento aggressivo. Gli errori se commessi in modo errato sono così gravi che dobbiamo limitarci all'ambito originale della domanda:

  1. qualche pezzo di codice nel mio grande sistema crea oggetti

La maggior parte dei poster qui presumono che tu stia dicendo che stai lavorando a un'interfaccia, se tale dovremmo vedere se ti viene consegnato l'intero oggetto o un oggetto alla volta.

Se non è più necessario un oggetto, è possibile assegnare null all'oggetto, ma se si sbaglia, viene generata un'eccezione puntatore null. Scommetto che puoi ottenere un lavoro migliore se usi NIO

Ogni volta che tu o io o chiunque altro riceviamo: " Per favore, ne ho bisogno orribilmente. " È un precursore quasi universale per la distruzione quasi totale di ciò su cui stai cercando di lavorare .... scrivici un piccolo codice di esempio, disinfettandolo da esso codice effettivo utilizzato e mostraci la tua domanda.

Non sentirti frustrato. Spesso ciò a cui si risolve è che il tuo dba sta usando un pacchetto acquistato da qualche parte e il design originale non è ottimizzato per enormi strutture di dati.

Questo è molto comune.


-1

FYI

La chiamata al metodo System.runFinalizersOnExit (true) garantisce che i metodi del finalizzatore vengano chiamati prima della chiusura di Java. Tuttavia, questo metodo è intrinsecamente non sicuro ed è stato deprecato. Un'alternativa è aggiungere "hook di arresto" con il metodo Runtime.addShutdownHook.

Masarrat Siddiqui


Il difetto con i ganci di arresto è che raramente funzionano davvero. La chiusura forzata non funziona, il codice di uscita diverso da zero non funziona e talvolta la JVM (ufficiale) semplicemente non le esegue finché ne hai bisogno.
ThePyroEagle il

-1

Esiste un modo indiretto per forzare il Garbage Collector. Devi solo riempire l'heap con oggetti temporanei fino al momento in cui verrà eseguito Garbage Collector. Ho creato una classe che forza il garbage collector in questo modo:

class GarbageCollectorManager {

    private static boolean collectionWasForced;
    private static int refCounter = 0;

    public GarbageCollectorManager() {
        refCounter++;
    }

    @Override
    protected void finalize() {
        try {
            collectionWasForced = true;
            refCounter--;
            super.finalize();   
        } catch (Throwable ex) {
            Logger.getLogger(GarbageCollectorManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public int forceGarbageCollection() {
        final int TEMPORARY_ARRAY_SIZE_FOR_GC = 200_000;
        int iterationsUntilCollected = 0;
        collectionWasForced = false;

        if (refCounter < 2) 
            new GarbageCollectorManager();

        while (!collectionWasForced) {
            iterationsUntilCollected++;
            int[] arr = new int[TEMPORARY_ARRAY_SIZE_FOR_GC];
            arr = null;
        }

        return iterationsUntilCollected;
    }

}

Uso:

GarbageCollectorManager manager = new GarbageCollectorManager();
int iterationsUntilGcExecuted = manager.forceGarbageCollection();

Non so quanto sia utile questo metodo, perché riempie costantemente heap, ma se si dispone di un'applicazione mission-critical che DEVE forzare GC - quando questo potrebbe essere il modo portatile Java per forzare GC.


Che cos'è "final int TEMPORARY_ARRAY_SIZE_FOR_GC = 200_000;"?
Koray Tugay,

Dimensione della matrice: quanti oggetti temporanei (int) verranno generati affinché GC inizi a funzionare.
Agnius Vasiliauskas,

È valido: "_" in un numero intero?
Koray Tugay,

1
Sì, i caratteri di sottolineatura in letterali numerici sono validi a partire da Java SE 7. Ciò è utile, ad esempio, come separatore delle migliaia nell'intero come in questo caso.
Agnius Vasiliauskas,

3
Non si dovrebbe mai eseguire tale codice in un sistema di produzione. Mentre questo codice viene eseguito in un thread qualsiasi altro thread può anche ottenere una OutOfMemoryException, invertire completamente l'intenzione di chiamarlo in primo luogo ....
Steffen Heil

-1

Vorrei aggiungere qualcosa qui. Si prega di non Java che gira su macchine virtuali e non su macchine reali. La macchina virtuale ha il suo modo di comunicare con la macchina. Potrebbe variare da sistema a sistema. Ora quando chiamiamo il GC chiediamo alla Macchina Virtuale di Java di chiamare il Garbage Collector.

Poiché Garbage Collector è con Virtual Machine, non possiamo costringerlo a fare una pulizia lì e poi. Piuttosto che mettiamo in coda la nostra richiesta con il Garbage Collector. Dipende dalla macchina virtuale, dopo un determinato periodo di tempo (questo può cambiare da sistema a sistema, generalmente quando la memoria di soglia allocata alla JVM è piena) la macchina effettiva libererà spazio. : D


La prima frase del secondo paragrafo è un non sequitur .
Marchese di Lorne,

-1

Il seguente codice è preso dal metodo assertGC (...). Cerca di forzare la raccolta non deterministica di immondizia.

   List<byte[]> alloc = new ArrayList<byte[]>();
   int size = 100000;
   for (int i = 0; i < 50; i++) {
        if (ref.get() == null) {
             // Test succeeded! Week referenced object has been cleared by gc.
             return;
        }
        try {
             System.gc();
        } catch (OutOfMemoryError error) {
             // OK
        }
        try {
             System.runFinalization();
        } catch (OutOfMemoryError error) {
             // OK
        }

        // Approach the jvm maximal allocatable memory threshold
        try {
             // Allocates memory.
             alloc.add(new byte[size]);

             // The amount of allocated memory is increased for the next iteration.
             size = (int)(((double)size) * 1.3);
        } catch (OutOfMemoryError error) {
             // The amount of allocated memory is decreased for the next iteration.
             size = size / 2;
        }

        try {
             if (i % 3 == 0) Thread.sleep(321);
        } catch (InterruptedException t) {
             // ignore
        }
   }

   // Test failed! 

   // Free resources required for testing
   alloc = null;

   // Try to find out who holds the reference.
   String str = null;
   try {
        str = findRefsFromRoot(ref.get(), rootsHint);
   } catch (Exception e) {
        throw new AssertionFailedErrorException(e);
   } catch (OutOfMemoryError err) {
        // OK
   }
   fail(text + ":\n" + str);

Fonte (ho aggiunto alcuni commenti per chiarezza): Esempio di NbTestCase


-1

È possibile provare a utilizzare Runtime.getRuntime().gc()o utilizzare il metodo di utilità System.gc()Nota: questi metodi non garantiscono GC. E il loro ambito di applicazione dovrebbe essere limitato a JVM piuttosto che gestirlo a livello di programmazione nell'applicazione.


2
Come spiegato nelle altre risposte, tali metodi non impongono un'esecuzione (completa) della garbage collection.
Flusso

-2

Se stai usando JUnit e Spring, prova ad aggiungere questo in ogni classe di test:

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.