Perché è una cattiva pratica chiamare System.gc ()?


326

Dopo aver risposto a una domanda su come forzare gli oggetti in Java (il ragazzo stava cancellando una HashMap da 1,5 GB) System.gc(), mi è stato detto che è una cattiva pratica chiamare System.gc()manualmente, ma i commenti non erano del tutto convincenti. Inoltre, nessuno sembrava osare votare, né sottovalutare la mia risposta.

Mi è stato detto che è una cattiva pratica, ma poi mi è stato anche detto che le esecuzioni dei garbage collector non fermano più sistematicamente il mondo e che potrebbero anche essere effettivamente utilizzate dalla JVM solo come un suggerimento, quindi sono un po ' in perdita.

Capisco che la JVM di solito conosce meglio di te quando deve recuperare la memoria. Capisco anche che preoccuparsi di alcuni kilobyte di dati è sciocco. Capisco anche che anche alcuni megabyte di dati non sono quelli di qualche anno fa. Ma ancora, 1,5 gigabyte? E sai che ci sono circa 1,5 GB di dati in giro in memoria; non è come se fosse uno sparo nel buio. È System.gc()sistematicamente negativo o c'è qualche punto in cui diventa ok?

Quindi la domanda è in realtà doppia:

  • Perché è o non è una cattiva pratica chiamare System.gc()? È davvero solo un suggerimento per la JVM in determinate implementazioni o è sempre un ciclo di raccolta completo? Esistono davvero implementazioni di Garbage Collector che possono fare il loro lavoro senza fermare il mondo? Per favore, fai luce sulle varie affermazioni che le persone hanno fatto nei commenti alla mia risposta .
  • Dov'è la soglia? Non è mai una buona idea chiamare System.gc()o ci sono momenti in cui è accettabile? Se è così, quali sono quei tempi?

4
Penso che un buon momento per chiamare System.gc () sia quando stai facendo qualcosa che è già un lungo processo di caricamento. Ad esempio, sto lavorando a un gioco e sto pianificando di chiamare System.gc () quando il gioco carica un nuovo livello, al termine del processo di caricamento. L'utente sta già aspettando un po 'e può valere la pena aumentare ulteriormente le prestazioni; ma inserirò anche un'opzione nella schermata di configurazione per disabilitare questo comportamento.
Ricket,

41
Bozho: nota la prima parola della domanda. PERCHÉ è buona norma non chiamarlo? La sola ripetizione di un mantra non spiega una dannata cosa.
SOLO IL MIO OPINIONE corretta,

1
Un altro caso è stato spiegato in java-monitor.com/forum/showthread.php?t=188 Dove spiega come può essere una cattiva pratica chiamare System.gc ()
Shirishkumar Bari

Risposte:


243

Il motivo per cui tutti dicono sempre di evitare System.gc()è che si tratta di un indicatore piuttosto valido di codice sostanzialmente non funzionante . Qualsiasi codice che dipende da esso per correttezza è certamente rotto; quelli che si basano su di esso per le prestazioni sono molto probabilmente rotti.

Non sai in quale tipo di garbage collector stai eseguendo. Ci sono certamente alcuni che non "fermano il mondo" come affermi, ma alcuni JVM non sono così intelligenti o per vari motivi (forse sono al telefono?) Non lo fanno. Non sai cosa sta per fare.

Inoltre, non è garantito fare nulla. La JVM potrebbe semplicemente ignorare completamente la tua richiesta.

La combinazione di "non sai cosa farà", "non sai nemmeno se sarà di aiuto" e "non dovresti chiamarlo comunque" sono i motivi per cui le persone sono così forti nel dire che in generale non dovresti chiamarlo. Penso che sia un caso di "se hai bisogno di chiedere se dovresti usare questo, non dovresti"


EDIT per rispondere ad alcune preoccupazioni dell'altra discussione:

Dopo aver letto il thread che hai collegato, ci sono alcune altre cose che vorrei sottolineare. Innanzitutto, qualcuno ha suggerito che la chiamata gc()potrebbe restituire memoria al sistema. Questo non è necessariamente vero: l'heap Java stesso cresce indipendentemente dalle allocazioni Java.

Come in, JVM manterrà la memoria (molte decine di megabyte) e farà crescere l'heap secondo necessità. Non restituisce necessariamente quella memoria al sistema anche quando si liberano oggetti Java; è perfettamente libero di conservare la memoria allocata da utilizzare per future allocazioni Java.

Per mostrare che è possibile che System.gc()non faccia nulla, visualizza:

http://bugs.sun.com/view_bug.do?bug_id=6668279

e in particolare che esiste un'opzione -XX: DisableExplicitGC VM.


1
Potresti essere in grado di costruire una strana configurazione di Rube Goldberg in cui il metodo in cui viene eseguito il GC influisce sulla correttezza del codice. Forse sta mascherando una strana interazione di threading, o forse un finalizzatore ha un effetto significativo sull'esecuzione del programma. Non sono del tutto sicuro che sia possibile, ma potrebbe essere, quindi ho pensato di menzionarlo.
Steven Schlansker,

2
@zneak potresti ad esempio aver inserito un codice critico nei finalizzatori (che è un codice fondamentalmente rotto)
Martin

3
Vorrei aggiungere che ci sono alcuni casi angolari in cui System.gc()è utile e potrebbe anche essere necessario. Ad esempio nelle applicazioni UI su Windows può velocizzare notevolmente il processo di ripristino di una finestra quando si chiama System.gc () prima di ridurre a icona la finestra (soprattutto quando rimane ridotta a icona per un po 'di tempo e parti del processo vengono scambiate con disco).
Joachim Sauer

2
@AndrewJanke Direi che il codice che usa WeakReferences per gli oggetti a cui vuoi aggrapparti non è corretto dall'inizio, garbage collection o no. Avresti lo stesso problema in C ++ con std::weak_ptr(anche se potresti notare il problema in una versione C ++ precedente a quello che avresti in una versione Java poiché la distruzione degli oggetti non sarebbe differita come la finalizzazione di solito).
JAB

2
@rebeccah Questo è un bug, quindi sì, lo definirei 'certamente rotto'. Il fatto che lo System.gc()risolva è una soluzione alternativa, non una buona pratica di codifica.
Steven Schlansker,

149

È già stato spiegato che la chiamata non system.gc() può fare nulla e che qualsiasi codice che "necessita" dell'esecuzione del Garbage Collector è interrotto.

Tuttavia, la ragione pragmatica che è una cattiva pratica chiamare System.gc()è che è inefficiente. E nel peggiore dei casi, è orribilmente inefficiente ! Lasciatemi spiegare.

Un tipico algoritmo GC identifica immondizia attraversando tutti gli oggetti non immondizia nell'heap e deducendo che qualsiasi oggetto non visitato deve essere immondizia. Da questo, possiamo modellare il lavoro totale di una garbage collection costituita da una parte che è proporzionale alla quantità di dati attivi e un'altra parte che è proporzionale alla quantità di immondizia; vale a dire work = (live * W1 + garbage * W2).

Supponiamo ora di fare quanto segue in un'applicazione a thread singolo.

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

La prima chiamata (prevediamo) (live * W1 + garbage * W2)funzionerà e eliminerà la spazzatura eccezionale.

La seconda chiamata (live* W1 + 0 * W2)funzionerà e non reclamerà nulla. In altre parole, abbiamo (live * W1)lavorato e non abbiamo ottenuto assolutamente nulla .

Possiamo modellare l'efficienza del collettore come la quantità di lavoro necessaria per raccogliere un'unità di immondizia; vale a dire efficiency = (live * W1 + garbage * W2) / garbage. Quindi, per rendere il GC il più efficiente possibile, dobbiamo massimizzare il valore di garbagequando eseguiamo il GC; cioè attendere fino a quando l'heap è pieno. (E inoltre, rendere l'heap il più grande possibile. Ma questo è un argomento separato.)

Se l'applicazione non interferisce (chiamando System.gc()), il GC attenderà fino a quando l'heap è pieno prima di essere eseguito, con conseguente raccolta efficiente dell'immondizia 1 . Ma se l'applicazione forza l'esecuzione del GC, è probabile che l'heap non sia pieno e il risultato sarà che la spazzatura verrà raccolta in modo inefficiente. E più spesso l'applicazione forza GC, tanto più inefficiente diventa il GC.

Nota: la spiegazione sopra menziona il fatto che un tipico GC moderno suddivide l'heap in "spazi", il GC può espandere dinamicamente l'heap, il set di lavoro dell'applicazione di oggetti non immondizia può variare e così via. Anche così, lo stesso principio di base si applica su tutta la linea a tutti i veri rifiuti 2 . È inefficiente forzare l'esecuzione del GC.


1 - Ecco come funziona il collector "throughput". I raccoglitori simultanei come CMS e G1 utilizzano criteri diversi per decidere quando avviare il Garbage Collector.

2 - Sto anche escludendo i gestori di memoria che usano esclusivamente il conteggio dei riferimenti, ma nessuna implementazione Java corrente utilizza questo approccio ... per una buona ragione.


45
+1 Buona spiegazione. Si noti tuttavia che questo ragionamento si applica solo se si tiene conto della velocità effettiva. Se si desidera ottimizzare la latenza in punti specifici, forzare GC può avere senso. Ad esempio (ipoteticamente parlando) in un gioco potresti voler evitare ritardi durante i livelli, ma non ti preoccupi dei ritardi durante il caricamento dei livelli. Quindi avrebbe senso forzare GC dopo il caricamento di livello. Diminuisce il rendimento complessivo, ma non è quello che stai ottimizzando.
sleske,

2
@sleske - quello che dici è vero. Tuttavia, eseguire il GC tra i livelli è una soluzione di cerotto ... e non risolve il problema di latenza se i livelli impiegano abbastanza tempo da dover eseguire il GC durante un livello comunque. Un approccio migliore consiste nell'utilizzare un garbage collector simultaneo (pausa bassa) ... se la piattaforma lo supporta.
Stephen C,

Non sono del tutto sicuro di cosa intendi per "ha bisogno del garbage collector per funzionare". Le condizioni che le applicazioni devono evitare sono; errori di allocazione che non possono mai essere soddisfatti e costi generali elevati del GC. Effettuare casualmente chiamate a System.gc () comporta spesso un sovraccarico GC elevato.
Kirk,

@Kirk - "Le chiamate casuali a System.gc () causano spesso un sovraccarico GC elevato." . Lo so. Lo stesso farebbe chiunque avesse letto e compreso la mia risposta.
Stephen C,

@Kirk - "Non sono del tutto sicuro di cosa intendi ..." - Intendo programmi in cui il comportamento "corretto" di un programma dipende dal funzionamento del GC in un determinato momento; ad es. per eseguire finalizzatori o per interrompere WeakReferences o SoftReferences. È una pessima idea farlo ... ma è di questo che sto parlando. Vedi anche la risposta di Steven Schlansker.
Stephen C,

47

Molte persone sembrano dirti di non farlo. Non sono d'accordo. Se, dopo un processo di caricamento di grandi dimensioni come caricare un livello, ritieni che:

  1. Hai molti oggetti che non sono raggiungibili e potrebbero non essere stati controllati. e
  2. Pensi che l'utente potrebbe sopportare un piccolo rallentamento a questo punto

non c'è nulla di male nel chiamare System.gc (). Lo guardo come la inlineparola chiave c / c ++ . È solo un indizio del fatto che tu, lo sviluppatore, hai deciso che il tempo / le prestazioni non sono così importanti come di solito e che alcuni potrebbero essere usati per recuperare la memoria.

Il consiglio di non fare affidamento sul fatto che fa qualcosa è corretto. Non fare affidamento sul fatto che funzioni, ma dare il suggerimento che ora è un momento accettabile per la raccolta va benissimo. Preferirei perdere tempo in un punto del codice in cui non importa (caricamento della schermata) rispetto a quando l'utente interagisce attivamente con il programma (come durante un livello di gioco).

C'è un momento in cui io vi costringo collezione: quando si tenta di scoprire è una particolare perdite di oggetti (sia codice nativo o grande, l'interazione di callback complesso Oh, e qualsiasi componente di interfaccia utente che così tanto come sguardi a Matlab.). Questo non dovrebbe mai essere usato nel codice di produzione.


3
+1 per GC durante l'analisi delle perdite di mem. Si noti che le informazioni sull'utilizzo dell'heap (Runtime.freeMemory () et al.) Sono veramente significative solo dopo aver forzato un GC, altrimenti dipenderebbe dall'ultima volta che il sistema si è preoccupato di eseguire un GC.
sleske,

4
non c'è nulla di male nel chiamare System.gc () potrebbe richiedere un stop the worldapproccio e questo è un vero danno, se succede
bestsss

7
C'è male nel chiamare il garbage collector in modo esplicito. Chiamare il GC nel momento sbagliato spreca i cicli della CPU. Tu (il programmatore) non hai abbastanza informazioni per determinare quando è il momento giusto ... ma JVM ha.
Stephen C,

Jetty effettua 2 chiamate a System.gc () dopo l'avvio e raramente l'ho visto fare la differenza.
Kirk,

Bene, gli sviluppatori di Jetty hanno un bug. Farebbe la differenza ... anche se la differenza è difficile da quantificare.
Stephen C,

31

Le persone hanno fatto un buon lavoro spiegando perché NON usare, quindi ti dirò un paio di situazioni in cui dovresti usarlo:

(I seguenti commenti si applicano a Hotspot in esecuzione su Linux con il raccoglitore CMS, in cui mi sento sicuro di dire che System.gc()in realtà invoca sempre una garbage collection completa).

  1. Dopo il lavoro iniziale di avvio dell'applicazione, potresti avere un terribile stato di utilizzo della memoria. Metà della tua generazione in carica potrebbe essere piena di immondizia, il che significa che sei molto più vicino al tuo primo CMS. Nelle applicazioni in cui è importante, non è una cattiva idea chiamare System.gc () per "ripristinare" il tuo heap allo stato iniziale dei dati attivi.

  2. Sulla stessa linea del numero 1, se si monitora attentamente l'utilizzo dell'heap, si desidera avere una lettura accurata dell'utilizzo della memoria di base. Se i primi 2 minuti di uptime dell'applicazione sono tutti inizializzati, i tuoi dati saranno incasinati a meno che tu non imponga (ahem ... "suggerire") l'intero gc in anticipo.

  3. Potresti avere un'applicazione progettata per non promuovere nulla alla generazione in carica mentre è in esecuzione. Ma forse è necessario inizializzare alcuni dati in anticipo che non sono così enormi da passare automaticamente alla generazione di ruolo. A meno che tu non chiami System.gc () dopo che tutto è stato impostato, i tuoi dati potrebbero rimanere nella nuova generazione fino al momento opportuno per essere promossi. All'improvviso la tua applicazione super-duper a bassa latenza e bassa GC viene colpita con una penalità ENORME (relativamente parlando, ovviamente) per la promozione di quegli oggetti durante le normali operazioni.

  4. Talvolta è utile disporre di una chiamata System.gc disponibile in un'applicazione di produzione per verificare l'esistenza di una perdita di memoria. Se sai che l'insieme di dati live all'ora X dovrebbe esistere in un certo rapporto con l'insieme di dati live all'ora Y, potrebbe essere utile chiamare System.gc () un tempo X e ora Y e confrontare l'utilizzo della memoria .


1
Per la maggior parte dei garbage collector generazionali, gli oggetti della nuova generazione devono sopravvivere a un certo numero (spesso configurabile) di garbage collection, quindi chiamare System.gc()una volta per forzare la promozione degli oggetti non ti fa guadagnare nulla. E sicuramente non vuoi chiamare System.gc()otto volte di fila e pregare che ora la promozione sia stata fatta e che i costi risparmiati di una promozione successiva giustifichino i costi di più GC completi. A seconda dell'algoritmo GC, la promozione di molti oggetti potrebbe non sostenere nemmeno i costi effettivi, in quanto riassegnerà la memoria alla vecchia generazione o la copia contemporaneamente ...
Holger

11

Questa è una domanda molto fastidiosa, e credo che contribuisca a molti si oppongono a Java, nonostante sia utile di una lingua.

Il fatto che non ci si possa fidare di "System.gc" per fare qualsiasi cosa è incredibilmente scoraggiante e può facilmente invocare "Paura, Incertezza, Dubbio" nella lingua.

In molti casi, è bello avere a che fare con picchi di memoria causati intenzionalmente prima che si verifichi un evento importante, il che indurrebbe gli utenti a pensare che il programma sia mal progettato / non risponde.

Avere la capacità di controllare la garbage collection sarebbe un ottimo strumento educativo, a sua volta migliorare la comprensione delle persone su come funziona la garbage collection e su come far sì che i programmi sfruttino il comportamento predefinito e il comportamento controllato.

Vorrei rivedere gli argomenti di questo thread.

  1. È inefficiente:

Spesso, il programma potrebbe non fare nulla e sai che non sta facendo nulla a causa del modo in cui è stato progettato. Ad esempio, potrebbe fare una sorta di lunga attesa con una grande finestra di messaggio di attesa e alla fine potrebbe anche aggiungere una chiamata per raccogliere la spazzatura perché il tempo per eseguirla impiegherà una frazione molto piccola del tempo del lunga attesa ma eviterà che gc agisca nel bel mezzo di un'operazione più importante.

  1. È sempre una cattiva pratica e indica un codice non funzionante.

Non sono d'accordo, non importa quale spazzatura hai. Il suo compito è rintracciare i rifiuti e pulirli.

Chiamando gc nei momenti in cui l'utilizzo è meno critico, si riducono le probabilità che funzioni quando la propria vita si basa sul codice specifico in esecuzione ma invece decide di raccogliere spazzatura.

Certo, potrebbe non comportarsi nel modo desiderato o previsto, ma quando si desidera chiamarlo, si sa che non sta succedendo nulla e l'utente è disposto a tollerare la lentezza / i tempi di inattività. Se System.gc funziona, fantastico! In caso contrario, almeno ci hai provato. Semplicemente non c'è alcun lato negativo a meno che il Garbage Collector non abbia effetti collaterali intrinseci che fanno qualcosa di orribilmente inaspettato a come si suppone che un Garbage Collector si comporti se invocato manualmente, e questo da solo provoca sfiducia.

  1. Non è un caso d'uso comune:

È un caso d'uso che non può essere ottenuto in modo affidabile, ma potrebbe esserlo se il sistema fosse progettato in questo modo. È come fare un semaforo e farlo in modo che alcuni / tutti i pulsanti dei semafori non facciano nulla, ti fa chiedere perché il pulsante è lì per cominciare, javascript non ha la funzione di garbage collection quindi noi non esaminalo tanto per quello.

  1. La specifica dice che System.gc () è un suggerimento che GC dovrebbe essere eseguito e la VM è libera di ignorarlo.

che cos'è un "suggerimento"? che cos'è "ignora"? un computer non può semplicemente dare suggerimenti o ignorare qualcosa, ci sono percorsi comportamentali rigorosi che possono essere dinamici che sono guidati dall'intento del sistema. Una risposta corretta dovrebbe includere ciò che il Garbage Collector sta effettivamente facendo, a livello di implementazione, che gli impedisce di eseguire la raccolta quando lo si richiede. La funzione è semplicemente no? C'è qualche tipo di condizioni che devo soddisfare? Quali sono queste condizioni?

Allo stato attuale, il GC di Java spesso sembra un mostro di cui non ti fidi. Non sai quando arriverà o verrà, non sai che cosa farà, come lo farà. Posso immaginare che alcuni esperti abbiano una migliore idea di come funziona la loro Garbage Collection in base alle istruzioni, ma la stragrande maggioranza spera semplicemente che "funzioni" e che fidarsi di un algoritmo apparentemente opaco per fare il lavoro per te sia frustrante.

C'è un grande divario tra la lettura di qualcosa o l'insegnamento di qualcosa, e in realtà vedere l'implementazione di esso, le differenze tra i sistemi e essere in grado di giocarci senza dover guardare il codice sorgente. Questo crea fiducia e sentimento di padronanza / comprensione / controllo.

Per riassumere, c'è un problema intrinseco con le risposte "questa funzione potrebbe non fare nulla, e non entrerò nei dettagli come dire quando fa qualcosa e quando non lo fa e perché non lo farà o non lo farà, spesso implica che è semplicemente contro la filosofia tentare di farlo, anche se l'intento alla base è ragionevole ".

Potrebbe essere giusto che Java GC si comporti nel modo in cui lo fa, oppure potrebbe non farlo, ma per capirlo, è difficile seguire veramente in quale direzione andare per ottenere una panoramica completa di ciò di cui puoi fidarti del GC e non farlo, quindi è troppo facile semplicemente diffidare della lingua, perché lo scopo di una lingua è avere un comportamento controllato fino a livello filosofico (è facile per un programmatore, in particolare i novizi, cadere in crisi esistenziali da determinati comportamenti di sistema / lingua) sono in grado di tollerare (e se non ci riesci, semplicemente non userai la lingua fino a quando non devi farlo) e più cose che non puoi controllare senza un motivo noto per cui non puoi controllarle sono intrinsecamente dannose.


9

L'efficienza GC si basa su una serie di euristiche. Ad esempio, un euristico comune è che gli accessi in scrittura agli oggetti di solito avvengono su oggetti creati non molto tempo fa. Un altro è che molti oggetti hanno una vita molto breve (alcuni oggetti saranno usati per molto tempo, ma molti saranno scartati qualche microsecondo dopo la loro creazione).

Chiamare System.gc()è come calciare il GC. Significa: "tutti quei parametri attentamente sintonizzati, quelle organizzazioni intelligenti, tutto lo sforzo che hai appena fatto nell'allocare e gestire gli oggetti in modo che le cose vadano bene, beh, lascia cadere tutto il lotto e ricomincia da zero". Si può migliorare le prestazioni, ma il più delle volte solo degrada le prestazioni.

Per utilizzare in System.gc()modo affidabile (*) è necessario sapere come funziona il GC in tutti i suoi dettagli. Tali dettagli tendono a cambiare un po 'se si utilizza una JVM di un altro fornitore o la versione successiva dello stesso fornitore o la stessa JVM ma con opzioni della riga di comando leggermente diverse. Quindi raramente è una buona idea, a meno che tu non voglia affrontare un problema specifico in cui controlli tutti quei parametri. Da qui la nozione di "cattiva pratica": non è vietato, il metodo esiste, ma raramente paga.

(*) Sto parlando di efficienza qui. System.gc()non romperà mai un programma Java corretto. Non evocherà memoria aggiuntiva che la JVM non avrebbe potuto ottenere diversamente: prima di lanciare una OutOfMemoryError, la JVM fa il lavoro System.gc(), anche se come ultima risorsa.


1
+1 per menzionare che System.gc () non impedisce OutOfMemoryError. Alcune persone ci credono.
sleske,

1
In realtà, potrebbe impedire OutOfMemoryError a causa della gestione di riferimenti software. Le SoftReferences create dopo l'ultima esecuzione GC non vengono raccolte nell'implementazione che conosco. Ma questi dettagli di implementazione sono soggetti a modifiche in qualsiasi momento e una sorta di bug e nulla su cui dovresti fare affidamento.
Maaartinus,

8

A volte ( non spesso! ) Sai davvero di più sull'utilizzo della memoria passata, attuale e futura, rispetto al tempo di esecuzione. Ciò non accade molto spesso e non vorrei mai affermare in un'applicazione Web mentre vengono offerte pagine normali.

Molti anni fa lavoro su un generatore di report, quello

  • Aveva un solo thread
  • Leggi la "richiesta di rapporto" da una coda
  • Caricato i dati necessari per il rapporto dal database
  • Generato il rapporto e inviato via e-mail.
  • Ripetuto per sempre, dormendo quando non c'erano richieste in sospeso.
  • Non ha riutilizzato alcun dato tra i rapporti e non ha fatto alcun incasso.

Innanzitutto poiché non era in tempo reale e gli utenti si aspettavano di attendere un rapporto, un ritardo durante l'esecuzione del GC non era un problema, ma dovevamo produrre rapporti con una velocità più rapida di quanto richiesto.

Guardando lo schema sopra del processo, è chiaro che.

  • Sappiamo che ci sarebbero pochissimi oggetti live subito dopo l'invio di un report via e-mail, poiché la successiva richiesta non era ancora iniziata con l'elaborazione.
  • È noto che il costo di esecuzione di un ciclo di garbage collection dipende dal numero di oggetti live, la quantità di garbage ha un effetto limitato sul costo di una corsa GC.
  • Che quando la coda è vuota non c'è niente di meglio da fare, eseguire il GC.

Quindi chiaramente valeva la pena fare una corsa GC ogni volta che la coda delle richieste era vuota; non c'era alcun aspetto negativo a questo.

Potrebbe valere la pena eseguire una corsa GC dopo che ogni rapporto è stato inviato per e-mail, poiché sappiamo che questo è un buon momento per una corsa GC. Tuttavia, se il computer avesse abbastanza RAM, si otterrebbero risultati migliori ritardando l'esecuzione del GC.

Questo comportamento è stato configurato in base all'installazione, per alcuni clienti che hanno abilitato un GC forzato dopo che ogni report ha notevolmente accelerato la protezione dei report. (Mi aspetto che ciò sia dovuto alla memoria insufficiente sul loro server e che esegua molti altri processi, quindi un GC forzato ha ridotto il paging.)

Non abbiamo mai rilevato un'installazione che non ha beneficiato dell'esecuzione forzata di GC ogni volta che la coda di lavoro era vuota.

Ma, sia chiaro, quanto sopra non è un caso comune.


3

Forse scrivo un codice scadente, ma mi sono reso conto che fare clic sull'icona del cestino su Eclipse e netbeans IDE è una "buona pratica".


1
Potrebbe essere così. Ma se Eclipse o NetBeans fossero programmati per chiamare System.gc()periodicamente, probabilmente il comportamento sarebbe fastidioso.
Stephen C

2

Innanzitutto, c'è una differenza tra spec e realtà. La specifica dice che System.gc () è un suggerimento che GC dovrebbe essere eseguito e la VM è libera di ignorarlo. La realtà è che la VM non ignorerà mai una chiamata a System.gc ().

Chiamare GC comporta un sovraccarico non banale per la chiamata e se lo fai in qualche momento casuale è probabile che non vedrai alcuna ricompensa per i tuoi sforzi. D'altra parte, è molto probabile che una raccolta innescata naturalmente recuperi i costi della chiamata. Se disponi di informazioni che indicano che è necessario eseguire un GC di quanto puoi effettuare la chiamata a System.gc () e dovresti vedere i vantaggi. Tuttavia, è la mia esperienza che ciò accade solo in alcuni casi limite poiché è molto improbabile che tu abbia abbastanza informazioni per capire se e quando chiamare System.gc ().

Un esempio elencato qui, colpendo il bidone della spazzatura nel tuo IDE. Se vai a una riunione, perché non colpirla. L'overhead non ti influenzerà e l'heap potrebbe essere pulito per quando torni. Fallo in un sistema di produzione e le frequenti chiamate da raccogliere lo porteranno a una brusca frenata! Anche chiamate occasionali come quelle effettuate da RMI possono disturbare le prestazioni.


3
"La realtà è che la VM non ignorerà mai una chiamata a System.gc ()." - Inesatto Leggi -XX: -DisableExplicitGC.
Stephen C,

1

Sì, chiamare System.gc () non garantisce che verrà eseguito, è una richiesta alla JVM che può essere ignorata. Dai documenti:

La chiamata al metodo gc suggerisce che la Java Virtual Machine impieghi sforzi per riciclare oggetti inutilizzati

È quasi sempre una cattiva idea chiamarlo perché la gestione automatica della memoria di solito sa meglio di te quando fare gc. Lo farà quando il suo pool interno di memoria libera è basso o se il sistema operativo richiede di restituire parte della memoria.

Potrebbe essere accettabile chiamare System.gc () se sai che aiuta. Con ciò intendo che hai testato e misurato a fondo il comportamento di entrambi gli scenari sulla piattaforma di distribuzione e puoi dimostrarlo. Tieni presente, tuttavia, che il gc non è facilmente prevedibile, può aiutare in una corsa e far male a un'altra.


<stroke> Ma anche da Javadoc: _Quando il controllo ritorna dalla chiamata del metodo, la macchina virtuale ha fatto del suo meglio per riciclare tutti gli oggetti scartati, che vedo come una forma più imperativa di ciò che hai pubblicato. </ stroke > Fanculo, c'è un bug report sul fatto che sia fuorviante. A partire da quale sa meglio, quali sono i danni di suggerire la JVM?
zneak,

1
Il danno è che fare la raccolta nel momento sbagliato può essere un enorme rallentamento. Il suggerimento che stai dando è probabilmente negativo. Per quanto riguarda il commento "best effort", provalo e vedi in uno strumento come JConsole. A volte, fare clic sul pulsante "Esegui GC" non fa nulla
tom

Siamo spiacenti di non essere d'accordo ma chiamare System.gc () in OpenJDK e tutto ciò che si basa su di esso (ad esempio HP) provoca sempre un ciclo di garbage collection. In effetti, questo sembra vero anche per l'implementazione di IBM J9
Kirk,

@Kirk - Errato: google e leggi -XX: -DisableExplicitGC.
Stephen C

0

Nella mia esperienza, l'utilizzo di System.gc () è effettivamente una forma di ottimizzazione specifica della piattaforma (in cui "piattaforma" è la combinazione di architettura hardware, sistema operativo, versione JVM e possibili altri parametri di runtime come la RAM disponibile), poiché il suo comportamento, sebbene approssimativamente prevedibile su una piattaforma specifica, può (e sarà) variare considerevolmente da una piattaforma all'altra.

Sì, ci sono situazioni in cui System.gc () migliorerà le prestazioni (percepite). Ad esempio è se i ritardi sono tollerabili in alcune parti dell'app, ma non in altre (l'esempio di gioco sopra citato, in cui si desidera che GC si verifichi all'inizio di un livello, non durante il livello).

Tuttavia, se aiuterà o farà del male (o non farà nulla) dipende fortemente dalla piattaforma (come definito sopra).

Quindi penso che sia valido come ottimizzazione specifica della piattaforma dell'ultima risorsa (cioè se altre ottimizzazioni delle prestazioni non sono sufficienti). Ma non dovresti mai chiamarlo solo perché ritieni che possa aiutare (senza benchmark specifici), perché è probabile che non lo farà.


0
  1. Poiché gli oggetti vengono allocati in modo dinamico utilizzando il nuovo operatore,
    è possibile che ci si chieda come tali oggetti vengano distrutti e la loro
    memoria venga rilasciata per una successiva riallocazione.

  2. In alcuni linguaggi, come C ++, gli oggetti allocati dinamicamente devono essere rilasciati manualmente mediante un operatore di eliminazione.

  3. Java ha un approccio diverso; gestisce automaticamente la deallocazione.
  4. La tecnica che realizza ciò si chiama garbage collection. Funziona in questo modo: quando non esistono riferimenti a un oggetto, si presume che quell'oggetto non sia più necessario e che la memoria occupata dall'oggetto possa essere recuperata. Non è necessario esplicitamente distruggere gli oggetti come in C ++.
  5. La garbage collection si verifica solo sporadicamente (se non del tutto) durante l'esecuzione del programma.
  6. Non si verificherà semplicemente perché esistono uno o più oggetti che non vengono più utilizzati.
  7. Inoltre, diverse implementazioni di runtime Java adotteranno approcci diversi alla garbage collection, ma per la maggior parte non dovresti pensarci mentre scrivi i tuoi programmi.
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.