Quando fa qualcosa System.gc ()?


118

So che la garbage collection è automatizzata in Java. Ma ho capito che se chiami il System.gc()tuo codice, la JVM può o meno decidere di eseguire la raccolta dei rifiuti a quel punto. Come funziona esattamente? Su quale base / parametri esattamente la JVM decide di fare (o non fare) un GC quando vede System.gc()?

Ci sono esempi nel qual caso è una buona idea metterlo nel codice?


4
Un concetto troppo complesso per tutti i dettagli qui, ma questo articolo dovrebbe aiutarti: chaoticjava.com/posts/how-does-garbage-collection-work
GEOCHET

Il comportamento esatto dipende dalla JVM, ovvero dipende dall'implementazione.
Thorbjørn Ravn Andersen

Risposte:


59

In pratica, di solito decide di fare una garbage collection. La risposta varia a seconda di molti fattori, come la JVM su cui stai eseguendo, in quale modalità si trova e quale algoritmo di garbage collection sta utilizzando.

Non dipenderei da esso nel tuo codice. Se la JVM sta per lanciare un OutOfMemoryError, la chiamata a System.gc () non lo fermerà, perché il garbage collector tenterà di liberare il più possibile prima di raggiungere quell'estremo. L'unica volta che l'ho visto usato in pratica è negli IDE dove è collegato a un pulsante su cui un utente può fare clic, ma anche lì non è molto utile.


3
puoi forzare una raccolta dei rifiuti in jconsole
Benedikt Waldvogel

25
@bene Puoi davvero "forzarlo"? Jconsole sta solo chiamando System.gc ()?
John Meagher,

4
Lo userei System.gc()durante una schermata di caricamento di un videogioco. A quel punto, non mi importerebbe davvero se la chiamata avesse cancellato tutto ciò che poteva o non avesse fatto nulla. Tuttavia, preferirei che "spendesse sforzi per riciclare oggetti inutilizzati" durante la schermata di caricamento, piuttosto che durante il gioco.
Kröw

30

L'unico esempio che riesco a pensare a dove ha senso chiamare System.gc () è quando si profila un'applicazione per cercare possibili perdite di memoria. Credo che i profiler chiamino questo metodo appena prima di scattare un'istantanea della memoria.


4
Sì, è quello che faccio anche io. I valori restituiti da Runtime.freeMemory (), ad esempio, sono realmente significativi solo dopo System.gc (), perché normalmente si desidera sapere quanta memoria è bloccata da istanze non recuperabili. Da usare ovviamente solo nel debugging / profilazione della memoria, mai in produzione.
sleske

No, va bene anche per molti altri scenari. Ad esempio, supponiamo che tu abbia un singolo modello che tiene conto del ciclo di vita. In onStop () se si annulla l'istanza, è una buona idea chiamare System.gc () per aiutarla.
portfoliobuilder

26

La specifica del linguaggio Java non garantisce che la JVM avvierà un GC quando si chiama System.gc(). Questa è la ragione di questo "può o non può decidere di fare un GC a quel punto".

Ora, se guardi il codice sorgente di OpenJDK , che è la spina dorsale di Oracle JVM, vedrai che una chiamata a System.gc()avvia un ciclo GC. Se usi un'altra JVM, come J9, devi controllare la loro documentazione per trovare la risposta. Ad esempio, la JVM di Azul ha un garbage collector che viene eseguito continuamente, quindi una chiamata a System.gc()non farà nulla

Qualche altra risposta menziona l'avvio di un GC in JConsole o VisualVM. Fondamentalmente, questi strumenti effettuano una chiamata remota a System.gc().

Di solito, non si desidera avviare un ciclo di raccolta dei rifiuti dal codice, poiché incasina la semantica dell'applicazione. La tua applicazione fa alcune cose aziendali, la JVM si occupa della gestione della memoria. Dovresti tenere separate queste preoccupazioni (non fare in modo che la tua applicazione gestisca la memoria, concentrati sugli affari).

Tuttavia, ci sono pochi casi in cui una chiamata a System.gc()potrebbe essere comprensibile. Considera, ad esempio, i microbenchmark. Nessuno vuole che un ciclo GC avvenga nel mezzo di un microbenchmark. Quindi è possibile attivare un ciclo GC tra ogni misurazione per assicurarsi che ogni misurazione inizi con un mucchio vuoto.


26

Non hai alcun controllo su GC in java: la VM decide. Non ho mai incontrato un caso in cui System.gc()è necessario . Poiché una System.gc()chiamata SUGGERISCE semplicemente che la VM esegua una garbage collection e che esegua anche una garbage collection COMPLETA (vecchia e nuova generazione in un mucchio multi-generazionale), allora può effettivamente causare il consumo di PIÙ cicli della CPU del necessario.

In alcuni casi, potrebbe avere senso suggerire alla VM di eseguire una raccolta completa ORA poiché potresti sapere che l'applicazione rimarrà inattiva per i prossimi minuti prima che si verifichi un sollevamento pesante. Ad esempio, subito dopo l'inizializzazione di molti oggetti temporanei durante l'avvio dell'applicazione (ad esempio, ho appena memorizzato nella cache una TONNELLATA di informazioni e so che non avrò molta attività per un minuto circa). Pensa a un IDE come l'avvio di eclipse: fa molto per inizializzare, quindi forse subito dopo l'inizializzazione ha senso fare un gc completo a quel punto.


17

Devi stare molto attento se chiami System.gc(). Chiamarlo può aggiungere problemi di prestazioni non necessari all'applicazione e non è garantito che esegua effettivamente una raccolta. In realtà è possibile disabilitare esplicitamente System.gc()tramite l'argomento java -XX:+DisableExplicitGC.

Consiglio vivamente di leggere i documenti disponibili su Java HotSpot Garbage Collection per dettagli più approfonditi sulla garbage collection.


La mia applicazione Android mi lancia sempre un'eccezione OutOfMemoryException. Quindi stavo pensando di utilizzare System.gc () nella funzione onDestroy della mia classe per cancellare tutti i riferimenti e gli oggetti indesiderati. È un buon approccio
Sagar Devanga

2
@Sagar Devanga È improbabile che chiamare manualmente il CC come descrivi sia di aiuto. Prima di lanciare un OOM, la JVM avrà già tentato di raccogliere i rifiuti per liberare memoria
Scrubbie

10

System.gc()è implementato dalla VM e ciò che fa è specifico per l'implementazione. L'implementatore potrebbe semplicemente tornare e non fare nulla, per esempio.

Per quanto riguarda quando emettere una raccolta manuale, l'unica volta in cui potresti volerlo fare è quando abbandoni una grande raccolta contenente un sacco di raccolte più piccole - una Map<String,<LinkedList>> per esempio - e vuoi provare a prendere il colpo di perf poi e lì, ma per la maggior parte, non dovresti preoccupartene. Il GC lo sa meglio di te - purtroppo - la maggior parte del tempo.


8

Se utilizzi buffer di memoria diretti, la JVM non esegue il GC per te anche se stai esaurendo la memoria diretta.

Se chiami ByteBuffer.allocateDirect()e ottieni un OutOfMemoryError, puoi scoprire che questa chiamata va bene dopo aver attivato manualmente un GC.


Stai dicendo che System.gc () raccoglie allocazioni dirette mentre la JVM normalmente non lo fa (prima di lanciare un OOM). Perché penso che sia sbagliato. L'unico motivo per cui un'allocazione potrebbe avere esito positivo dopo un GC esplicito è il tempo aggiuntivo e qualche probabilità in più di un oggetto in-heap con un finalizzatore che viene liberato (e che libera alcuni oggetti allocati direttamente).
eckes il

Nell'ultima versione di Java 6, il codice in allocateBuffer eseguirà sempre un System.gc () prima di lanciare un OutOfMemoryError. Nel codice di finalizzazione ha una scorciatoia per la Cleanersquale utilizza la memoria diretta. cioè non viene liberato dal thread del finalizzatore in quanto può essere troppo lento. Anche così, è possibile ottenere un OOME se si creano regioni di memoria diretta particolarmente veloci. La soluzione è non farlo e riutilizzare le regioni di memoria diretta dove è possibile.
Peter Lawrey

1
grazie, questo suona più come la mia esperienza. Quindi immagino che la risposta sia corretta solo per le versioni precedenti di Java.
Eckes

5

La maggior parte delle JVM darà il via a un GC (a seconda delle opzioni -XX: DiableExplicitGC e -XX: + ExplicitGCInvokesConcurrent). Ma la specifica è solo meno definita per consentire implementazioni migliori in seguito.

La specifica necessita di chiarimenti: Bug # 6668279: (spec) System.gc () dovrebbe indicare che non consigliamo l'uso e non garantiamo il comportamento

Internamente il metodo gc è utilizzato da RMI e NIO e richiedono l'esecuzione sincrona, che: questo è attualmente in discussione:

Bug # 5025281: Consenti a System.gc () di attivare raccolte complete simultanee (non stop-the-world)


3

Garbage Collectionè buono Java, se stiamo eseguendo software codificato in java in Desktop / laptop / server. È possibile chiamare System.gc()o Runtime.getRuntime().gc()in Java.

Tieni presente che nessuna di queste chiamate è garantita per fare qualcosa. Sono solo un suggerimento per la jvm di eseguire Garbage Collector. Dipende dalla JVM se esegue o meno il GC. Quindi, risposta breve: non sappiamo quando funziona. Risposta più lunga: JVM eseguirà gc se ha tempo per quello.

Credo, lo stesso vale per Android. Tuttavia, questo potrebbe rallentare il tuo sistema.


JVM eseguirà gc se ha tempo per quello : non sempre vero, Jvm può eseguire gc quando la memoria di Eden è piena.
abdelmouheimen

2

Normalmente, la VM eseguirà automaticamente una garbage collection prima di lanciare un'eccezione OutOfMemoryException, quindi l'aggiunta di una chiamata esplicita non dovrebbe aiutare se non in quanto forse sposta il colpo di prestazioni a un momento precedente nel tempo.

Tuttavia, penso di aver riscontrato un caso in cui potrebbe essere rilevante. Non ne sono sicuro, perché devo ancora verificare se ha qualche effetto:

Quando si mappa un file in memoria, credo che la chiamata map () generi un'eccezione IOException quando non è disponibile un blocco di memoria sufficientemente grande. Una garbage collection appena prima del file map () potrebbe aiutare a prevenirlo, credo. Cosa ne pensi?


2

non possiamo mai forzare la raccolta dei rifiuti. System.gc suggerisce solo vm per la garbage collection, tuttavia, a che ora viene eseguito il meccanismo, nessuno lo sa, questo è come stabilito dalle specifiche JSR.


2

C'è molto da dire nel prendere il tempo per testare le varie impostazioni di garbage collection, ma come menzionato sopra di solito non è utile farlo.

Attualmente sto lavorando a un progetto che coinvolge un ambiente con memoria limitata e una quantità relativamente grande di dati: ci sono alcuni grandi pezzi di dati che spingono il mio ambiente al limite, e anche se sono stato in grado di ridurre l'utilizzo della memoria così che in teoria dovrebbe funzionare bene, riceverei ancora errori di spazio sull'heap --- le opzioni GC dettagliate mi hanno mostrato che stava tentando di raccogliere i rifiuti, ma senza alcun risultato. Nel debugger, potrei eseguire System.gc () e abbastanza sicuro ci sarebbe "abbondanza" di memoria disponibile ... non molto extra, ma abbastanza.

Di conseguenza, l'unica volta che la mia applicazione chiama System.gc () è quando sta per entrare nel segmento di codice dove verranno allocati grandi buffer necessari per l'elaborazione dei dati, e un test sulla memoria libera disponibile indica che non lo sono garantito per averlo. In particolare, sto osservando un ambiente da 1 GB in cui almeno 300 MB sono occupati da dati statici, con la maggior parte dei dati non statici relativi all'esecuzione tranne quando i dati in elaborazione sono almeno 100-200 MB a la fonte. Fa tutto parte di un processo di conversione automatica dei dati, quindi tutti i dati esistono per periodi di tempo relativamente brevi a lungo termine.

Sfortunatamente, sebbene siano disponibili informazioni sulle varie opzioni per la messa a punto del garbage collector, sembra in gran parte un processo sperimentale e le specifiche di livello inferiore necessarie per capire come gestire queste situazioni specifiche non sono facilmente ottenibili.

Detto questo, anche se sto utilizzando System.gc (), ho comunque continuato a sintonizzarmi utilizzando i parametri della riga di comando e sono riuscito a migliorare il tempo di elaborazione complessivo della mia applicazione di una quantità relativamente significativa, nonostante non sia stato in grado di superare il ostacolo posto dal lavorare con i blocchi di dati più grandi. Detto questo, System.gc () è uno strumento ... uno strumento molto inaffidabile, e se non stai attento a come lo usi, vorresti che non funzionasse il più delle volte.


2

Se vuoi sapere se il tuo System.gc()viene chiamato, puoi con il nuovo Java 7 update 4 ricevere una notifica quando JVM esegue Garbage Collection.

Non sono sicuro al 100% che la classe GarbageCollectorMXBean sia stata introdotta nell'aggiornamento 4 di Java 7, perché non sono riuscito a trovarlo nelle note di rilascio, ma ho trovato le informazioni nel sito javaperformancetuning .com


0

Non riesco a pensare a un esempio specifico quando è bene eseguire GC esplicito.

In generale, l'esecuzione di GC espliciti può effettivamente causare più danni che benefici, perché un gc esplicito attiverà una raccolta completa, che richiede molto più tempo mentre attraversa ogni oggetto. Se questo gc esplicito finisce per essere chiamato ripetutamente, potrebbe facilmente portare a un'applicazione lenta poiché viene impiegato molto tempo per eseguire GC completi.

In alternativa, se si esamina l'heap con un analizzatore di heap e si sospetta che un componente di libreria stia chiamando GC espliciti, è possibile disattivarlo aggiungendo: gc = -XX: + DisableExplicitGC ai parametri JVM.


2
Chiamiamo System.gc () per provare a chiudere i nostri oggetti MappedByteBuffer che altrimenti non sarebbero chiusi, e quindi non disponibili per altri processi o per il troncamento di file, anche se chiamiamo 'close ()' sugli oggetti. Purtroppo ancora non sempre li chiude.
Tim Cooper

0

Secondo Thinking in Java di Bruce Eckel, un caso d'uso per la chiamata esplicita System.gc () è quando si desidera forzare la finalizzazione, ovvero la chiamata al metodo finalize .


NB: esiste un proprio metodo per forzare l'esecuzione della finalizzazione. (Il problema in realtà non è nell'esecuzione dei finalizzatori, ma nel riconoscere che un oggetto può essere finalizzato perché non è referenziato)
eckes

-1

mentre system.gc funziona, fermerà il mondo: tutti i respones vengono fermati in modo che il garbage collector possa scansionare ogni oggetto per verificare se è necessario cancellarlo. se l'applicazione è un progetto web, tutte le richieste vengono interrotte fino al termine di gc e questo farà sì che il tuo progetto web non possa funzionare in un monente.

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.