traduzione - system gc() in java




Perché java aspetta così tanto tempo per eseguire il garbage collector? (4)

Avevo un'app che produceva un grafico del genere e funzionava come tu descrivi. Stavo usando il raccoglitore CMS (-XX: + UseConcMarkSweepGC). Ecco cosa stava succedendo nel mio caso.

Non avevo abbastanza memoria configurata per l'applicazione, quindi nel tempo stavo correndo in problemi di frammentazione nell'heap. Ciò ha causato GC con una frequenza maggiore e maggiore, ma in realtà non ha lanciato un OOME o un fail di CMS al serial collector (cosa che dovrebbe fare in quel caso) perché le statistiche mantengono solo il tempo di pausa dell'applicazione (blocchi GC) il mondo), il tempo concomitante dell'applicazione (GC viene eseguito con i thread dell'applicazione) viene ignorato per questi calcoli. Ho sintonizzato alcuni parametri, principalmente gli ho dato un intero heap più carico (con un nuovo spazio molto grande), set -XX: CMSFullGCsBeforeCompaction = 1 e il problema si è interrotto.

Sto costruendo un'app web Java, usando il Play! Quadro . Lo sto ospitando su playapps.net . Sono rimasto perplesso per un po 'sui grafici forniti dal consumo di memoria. Ecco un esempio:

Il grafico deriva da un periodo di attività costante ma nominale. Non ho fatto nulla per innescare la diminuzione della memoria, quindi presumo che ciò sia avvenuto perché il garbage collector ha funzionato poiché ha quasi raggiunto il consumo di memoria consentito.

Le mie domande:

  • È giusto presumere che la mia applicazione non abbia una perdita di memoria, poiché sembra che tutta la memoria sia correttamente recuperata dal garbage collector quando viene eseguita?
  • (dal titolo) Perché java sta aspettando fino all'ultimo secondo possibile per eseguire il garbage collector? Sto assistendo a un significativo peggioramento delle prestazioni mentre il consumo di memoria sale al quarto superiore del grafico.
  • Se le mie affermazioni sopra sono corrette, allora come posso risolvere il problema? Gli altri post che ho letto su SO sembrano opposti alle chiamate a System.gc() , che vanno dal neutro ("è solo una richiesta per eseguire GC, quindi la JVM può semplicemente ignorarti") a completamente opposto ("codice che si basa su System.gc() è fondamentalmente rotto "). O sono fuori base qui, e dovrei cercare difetti nel mio codice che sta causando questo comportamento e perdite di prestazioni intermittenti?

AGGIORNARE
Ho aperto una discussione su PlayApps.net facendo riferimento a questa domanda e menzionando alcuni dei punti qui; in particolare il commento di @ Affe riguardante le impostazioni per un GC completo viene impostato in modo molto conservativo e il commento di @ G_H sulle impostazioni per la dimensione iniziale e massima dell'heap.

Ecco un link alla discussione , anche se sfortunatamente hai bisogno di un account playapp per vederlo.

Riferirò il feedback qui quando ho capito; grazie mille a tutti per le vostre risposte, ho già imparato molto da loro!

Risoluzione
Il supporto per PlayApps, che è ancora ottimo, non ha molti suggerimenti per me, il loro unico pensiero è che se stavo usando la cache estesamente, questo potrebbe tenere gli oggetti in vita più a lungo del necessario, ma non è così. Ho ancora imparato una tonnellata (wooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo


Come avrai notato, questo non ha alcun effetto su di te. La garbage collection viene attivata solo se la JVM ritiene che sia necessario eseguirla e ciò accade per motivi di ottimizzazione, non è possibile fare molte piccole raccolte se è possibile creare una singola raccolta completa e effettuare una pulizia completa.

L'attuale JVM contiene alcuni algoritmi davvero interessanti e l'id della raccolta dei rifiuti stessi divisa in 3 diverse regioni, qui puoi trovare molto di più su questo, ecco un esempio:

Tre tipi di algoritmi di raccolta

HotSpot JVM fornisce tre algoritmi GC, ciascuno sintonizzato per uno specifico tipo di raccolta all'interno di una specifica generazione. L'insieme di copie (noto anche come scavenge) ripulisce rapidamente oggetti di breve durata nell'heap di nuova generazione. L'algoritmo mark-compact utilizza una tecnica più lenta e più robusta per raccogliere oggetti di maggiore durata nell'heap di vecchia generazione. L'algoritmo incrementale tenta di migliorare la raccolta di vecchia generazione eseguendo un solido GC riducendo al minimo le pause.

Copia / raccolta di raccolta

Utilizzando l'algoritmo di copia, la JVM recupera la maggior parte degli oggetti nello spazio oggetti di nuova generazione (noto anche come eden) semplicemente creando piccoli scavenging - un termine Java per la raccolta e la rimozione dei rifiuti. Gli oggetti a vita più lunga vengono infine copiati o mantenuti nel vecchio spazio oggetti.

Collezione Mark-compact

Man mano che più oggetti diventano di proprietà, il vecchio spazio dell'oggetto inizia a raggiungere la massima occupazione. L'algoritmo mark-compact, utilizzato per raccogliere oggetti nel vecchio spazio oggetti, ha requisiti diversi rispetto all'algoritmo di raccolta copie utilizzato nel nuovo spazio oggetti.

L'algoritmo mark-compact dapprima esegue la scansione di tutti gli oggetti, contrassegnando tutti gli oggetti raggiungibili. Compatta quindi tutti gli spazi vuoti rimanenti di oggetti morti. L'algoritmo mark-compact occupa più tempo rispetto all'algoritmo di raccolta delle copie; tuttavia, richiede meno memoria ed elimina la frammentazione della memoria.

Collezione incrementale (treno)

La copia di nuova generazione / scavenger e gli algoritmi mark-compact di vecchia generazione non possono eliminare tutte le pause JVM. Queste pause sono proporzionali al numero di oggetti dal vivo. Per soddisfare la necessità di un GC senza pause, HotSpot JVM offre anche una raccolta incrementale o in treno.

La raccolta incrementale interrompe le vecchie pause delle raccolte di oggetti in molte piccole pause anche con aree di oggetti di grandi dimensioni. Invece di una nuova e una vecchia generazione, questo algoritmo ha una generazione intermedia che comprende molti piccoli spazi. C'è un sovraccarico associato alla raccolta incrementale; potresti vedere un degrado della velocità del 10%.

I parametri -Xincgc e -Xnoincgc controllano l'utilizzo della raccolta incrementale. La prossima versione di HotSpot JVM, versione 1.4, tenterà un GC continuo, senza pause, che probabilmente sarà una variazione dell'algoritmo incrementale. Non discuterò la raccolta incrementale poiché cambierà presto.

Questo garbage collector generazionale è una delle soluzioni più efficienti che abbiamo per il problema al giorno d'oggi.


Probabilmente hai perdite di memoria che vengono cancellate ogni 24 ore.


Qualsiasi risposta dettagliata dipenderà da quale garbage collector si sta utilizzando, ma ci sono alcune cose che sono sostanzialmente le stesse su tutti i GC (moderni, solari / oracle).

Ogni volta che vedi l'utilizzo nel grafico si riduce, si tratta di una garbage collection. L'unico modo in cui l'heap viene liberato è attraverso la garbage collection. Il fatto è che ci sono due tipi di garbage collection, minori e completi. L'heap viene diviso in due "aree" di base. Giovani e di ruolo. (Ci sono molti altri sottogruppi nella realtà). Qualunque cosa occupi spazio in Young ed è ancora in uso quando il GC secondario arriva per liberare un po 'di memoria, verrà "promosso" in tenured. Una volta che qualcosa fa il salto di qualità, rimane in sospeso a tempo indeterminato fino a quando l'heap non ha spazio libero e è necessaria una raccolta completa dei rifiuti.

Quindi un'interpretazione di quel grafico è che la tua giovane generazione è piuttosto piccola (per impostazione predefinita può essere una percentuale relativamente piccola dell'heap totale su alcune JVM) e stai mantenendo gli oggetti "vivi" per tempi comparativamente molto lunghi. (forse stai tenendo dei riferimenti a loro nella sessione web?) Quindi i tuoi oggetti sono raccolte di garbage 'sopravviventi' fino a quando non vengono promossi in uno spazio protetto, dove restano indefinitamente finché la JVM non è in buone condizioni e senza memoria.

Di nuovo, questa è solo una situazione comune che si adatta ai dati che hai. Avrebbero bisogno di tutti i dettagli sulla configurazione JVM e sui log del GC per capire veramente cosa sta succedendo.





playframework