Articolo originale: Garbage Collection in Java – What is GC and How it Works in the JVM

Nel mio precedente articolo, ho trattato la Java Virtual Machine (JVM) e ne ho descritto l'architettura. Inoltre, ho introdotto brevemente il Java Garbage Collector (GC, letteralmente "raccoglitore della spazzatura", netturbino) come parte del componente del motore di esecuzione.

In questo articolo, approfondiremo l'argomento del Garbage Collector, il suo funzionamento e i diversi tipi di GC disponibili nell'ambiente Java, assieme ai loro vantaggi. Saranno inoltre inclusi nella spiegazione anche alcuni dei nuovi Garbage Collector sperimentali e disponibili solamente nelle più recenti release Java.

Che cos'è la Garbage Collection in Java?

La Garbage Collection (la raccolta della spazzatura) è il processo di recupero della memoria di runtime allocata ma inutilizzata che viene attuato mediante la distruzione degli oggetti inutilizzati.

In linguaggi come C e C++, è il programmatore a essere responsabile sia per la creazione che la distruzione degli oggetti. Talvolta, il programmatore può dimenticare di eliminare oggetti obsoleti e in questo modo la memoria allocata per accoglierli non viene liberata. La memoria utilizzata continua a crescere fino a quando non rimane più memoria da allocare. Applicazioni con questo comportamento soffrono di "memory leak" (falle, perdite di memoria).

Superata una certa soglia, non rimane più memoria libera in quantità sufficiente per la creazione di nuovi oggetti e l'intero programma termina in modo anomalo a causa di eccezioni di tipo OutOfMemoryError.

Si possono utilizzare metodi come free() in C e delete() in C++ per eseguire esplicitamente la Garbage Collection. In Java, la garbage collection avviene in modo automatico durante il ciclo di vita di un programma. Questa caratteristica elimina la necessità di de-allocare memoria e di conseguenza protegge dalle memory leak.

La Java Garbage Collection è il processo attraverso il quale Java esegue la gestione automatica della memoria. I programmi Java si compilano in bytecode che può essere eseguito su una Java Virtual Machine (JVM).

Quando i programmi Java sono eseguiti sulla JVM, gli oggetti sono creati sull'heap, che è un'area della memoria dedicata al programma.

Durante il ciclo di vita di un'applicazione Java, nuovi oggetti sono creati e rilasciati. Alla fine, alcuni oggetti diventeranno obsoleti. In particolare, possiamo dire che in un qualsiasi momento nel tempo, la memoria contenuta nell'heap è occupata da due tipi di oggetti:

  • Vivi - questi oggetti sono utilizzati e referenziati in altre parti del programma
  • Morti - questi oggetti non sono più utilizzati né referenziati in una qualunque parte del programma

Il garbage collector individua gli oggetti obsoleti non più utilizzati per poi eliminarli liberando memoria.

Come de-referenziare un oggetto in Java

L'obiettivo principale della Garbage Collection è liberare memoria dall'heap distruggendo tutti quegli oggetti che non contengono un riferimento. Quando non vi sono riferimenti a un oggetto, si assume che sia obsoleto e non più necessario. In questo modo la memoria occupata dall'oggetto può essere reclamata.

Vi sono vari modi attraverso i quali le referenze verso un oggetto possono essere rilasciate per renderlo un potenziale candidato scelto dalla Garbage Collection. Alcuni di questi sono i seguenti:

Assegnare null ad un riferimento

Student student = new Student();
student = null;

Assegnare un riferimento a un altro

Student studentOne = new Student();
Student studentTwo = new Student();
studentOne = studentTwo; // ora il primo oggetto referenziato da studentOne è disponibile per il passaggio del Garbage Collector

Utilizzare un oggetto anonimo

register(new Student());

Come funziona la Garbage Collection in Java?

La Garbage Collection in ambiente Java è un processo automatico. Il programmatore non ha l'onere di indicare esplicitamente quali oggetti destinare alla cancellazione.

L'implementazione delle garbage collection vive all'interno della JVM. Ciascuna JVM può implementare la propria versione di garbage collection. Tuttavia, deve sempre rispettare la specifica standard della JVM che richiede di lavorare con gli oggetti presenti nella porzione heap della memoria, segnalando o identificando gli oggetti divenuti irraggiungibili e distruggendoli con la compattazione.

Cosa sono le radici della Garbage Collection in Java?

I garbage collector funzionano basandosi sul concetto di radici della Garbage Collection (GC Roots) per distinguere gli oggetti vivi da quelli morti.

Esempi di tali radici son i seguenti:

  • Classi caricate dal class loader di sistema (non da class loader custom)
  • Thread attivi
  • Variabili locali e parametri dei metodi attualmente in esecuzione
  • Variabili locali e parametri dei metodi dei metodi JNI
  • Riferimenti JNI globali
  • Oggetti utilizzati come monitor per la sincronizzazione
  • Oggetti utilizzati dalla JVM la quale li trattiene impedendo che finiscano distrutti dalla garbage collection

Il garbage collector attraversa l'intero grafo nella memoria, iniziando dalle radici e seguendo i riferimenti da queste verso gli altri oggetti.

image-76

Le fasi della Garbage Collection in Java

Un'implementazione standard di Garbage Collection comprende tre fasi:

Marcatura degli oggetti vivi

In questo step, il GC individua tutti gli oggetti vivi in memoria attraversando il grafo degli oggetti.

Quando il GC visita un oggetto, lo marca come accessibile e quindi lo classifica come vivo. Ciascun oggetto visitato dal garbage collector è considerato vivo. Analogamente, tutti gli oggetti che non sono raggiungibili a partire dalle radici sono considerati oggetti morti e potenziali candidati per la garbage collection.

image-82

Pulizia degli oggetti obsoleti

Dopo la fase di marcatura, siamo nella condizione in cui lo spazio di memoria è occupato dagli oggetti vivi (visitati) e dagli oggetti morti (non raggiungibili, non visitati). La fase di pulizia della memoria ha l'effetto di svuotare tutti i frammenti di memoria che prima erano occupati dagli oggetti morti.

image-83

Compattazione degli oggetti rimanenti in memoria

Gli oggetti morti rimossi durante la fase di pulizia potrebbero non aver occupato segmenti di memoria adiacenti. Pertanto, è probabile che si finisca per ottenere uno spazio di memoria frammentato.

La memoria può essere compattata dopo la cancellazione degli oggetti morti, in modo che gli oggetti rimanenti occupino un blocco contiguo di memoria che comincia all'inizio dell'area di heap.

Il processo di compattazione rende più facile allocare la memoria per i nuovi oggetti in modo sequenziale, assegnando indirizzi via via crescenti.

image-85

Che cosa si intende per Garbage Collection generazionale in Java?

I Java Garbage Collector implementano una strategia di garbage collection generazionale che classifica gli oggetti distinguendoli in classi di età.

Il compito di marcare e compattare tutti gli oggetti presenti in una JVM è un processo inefficiente. Con l'aumentare degli oggetti allocati, la lista degli oggetti cresce, e si allungano i tempi richiesti dalla garbage collection. L'analisi empirica delle applicazioni ha dimostrato che la maggior parte degli oggetti in Java ha vita breve.

ObjectLifetime
Source: oracle.com

Nell'esempio qui sopra, l'asse Y indica il numero di byte sopravvissuti o mantenuti, mentre l'asse X rappresenta il numero di byte allocati al passare del tempo. Come si può notare, in un intervallo di tempo abbastanza lungo, rimangono sempre meno oggetti allocati.

Infatti, la gran parte degli oggetti ha una vita molto breve, come dimostrato dai valori più alti vicino all'asse Y del grafico. Questo avviene perché Java classifica gli oggetti in generazioni e applica i cicli di garbage collection coerentemente con tale classificazione.

L'area di memoria riservata all'heap nella JVM è suddivisa in tre sezioni:

image-70

Generazione Giovane

I nuovi oggetti iniziano il loro ciclo di vita nella Generazione Giovane (Young Generation). A sua volta, la generazione giovane viene suddivisa in:

  • Spazio Eden - tutti i nuovi oggetti partono in questo spazio quando ricevono l'allocazione di memoria iniziale
  • Spazi destinati ai sopravvissuti (FromSpace e ToSpace) - gli oggetti vengono trasferiti qui dallo spazio Eden dopo essere sopravvissuti al primo ciclo di garbage collection.

Il momento in cui il garbage collector rimuove gli oggetti dall'area di Generazione Giovane, è indicato come evento di garbage collection minore.

Quando lo spazio Eden è stato popolato con gli oggetti, viene eseguito un ciclo di GC minore.  Tutti gli oggetti marcati come morti sono rimossi, mentre tutti gli oggetti marcati come vivi sono trasferiti ad uno degli spazi destinati ai sopravvissuti. L'evento di GC minore verifica anche gli oggetti presenti negli spazi dei sopravvissuti e li trasferisce da uno spazio all'altro.

Esaminiamo la seguente sequenza di esempio:

  1. Tutti gli oggetti sono inseriti nello spazio Eden (sia vivi che morti)
  2. Viene eseguito il ciclo di GC minore - tutti gli oggetti morti vengono estratti dallo spazio Eden. Tutti gli oggetti vivi sono trasferiti allo spazio dei sopravvissuti S1 (FromSpace). A questo punto lo spazio Eden è stato svuotato e lo spazio dei sopravvissuti S2 è vuoto.
  3. I nuovi oggetti sono creati e aggiunti allo spazio Eden. Alcuni oggetti in S1 e nello spazio Eden vengono classificati come morti.
  4. Viene eseguito un ciclo di GC minore - tutti gli oggetti morti sono rimossi da Eden ed S1. Tutti gli oggetti vivi sono trasferiti a S2 (ToSpace). Lo spazio Eden e lo spazio S1 adesso sono stati svuotati.

Pertanto, in qualsiasi momento, uno dei due spazi destinati agli oggetti sopravvissuti è sempre vuoto. Quando gli oggetti sopravvissuti raggiungono una certa soglia di trasferimenti da uno spazio dei sopravvissuti all'altro, vengono inviati alla Generazione Vecchia (Old Generation).

Per impostare la dimensione della Generazione Giovane è possibile usare l'apposito flag -Xmn .

Generazione Vecchia

Gli oggetti che sopravvivono più a lungo vengono ad un certo punto spostati dalla Generazione Giovane alla Generazione Vecchia. Quest'ultima è anche conosciuta come la Generazione "di ruolo" e contiene gli oggetti che sono stati mantenuti negli spazi dei sopravvissuti per un tempo relativamente lungo.

È definito un preciso valore di soglia per il mandato di ciascun oggetto che determina il numero dei cicli di garbage collection a cui l'oggetto deve sopravvivere prima che venga trasferito allo spazio della Generazione Vecchia.

Quando gli oggetti presenti nella Generazione Vecchia vengono raccolti dal garbage collector, è indicato come evento di garbage collection maggiore.

Per impostare la dimensione iniziale e quella massima raggiungibile dall'area Heap della memoria è possibile agire, rispettivamente, sugli appositi flag -Xms e -Xmx.

Dato il funzionamento della garbage collection generazionale utilizzata in Java, più è alto il numero di cicli di garbage collection a cui un evento sopravvive, più avanti in termini di indirizzo verrà spostato all'interno dell'heap. Inizia la sua vita nella Generazione Giovane e, alla fine, potrà giungere alla Generazione "di ruolo", se sopravvivrà ad un numero di cicli sufficiente.

Consideriamo l'esempio seguente per comprendere la promozione degli oggetti attraverso gli spazi e le generazioni:

Quando un oggetto viene creato, è inserito inizialmente nello Spazio Eden all'interno della Generazione Giovane. Dopo l'intervento di un ciclo di garbage collection minore, gli oggetti vivi presenti nello spazio Eden sono promossi nello spazio dei sopravvissuti FromSpace. Quando si verifica il successivo ciclo di garbage collection minore, gli oggetti vivi presenti sia nello spazio Eden che nel FromSpace sono trasferiti al ToSpace.

Questa successione di eventi è ripetuta per un numero predefinito di iterazioni, raggiunto il quale, qualora l'oggetto risultasse ancora utilizzato, viene destinato alla Generazione Vecchia a partire dal ciclo di garbage collection successivo.

Generazione Permanente

I metadati come classi e metodi sono memorizzati nella Generazione Permanente. Viene popolata dalla JVM al runtime sulla base delle classi in uso dall'applicazione. Le classi presenti nella Generazione Permanente che non risultano più in uso potrebbero essere raccolte dal garbage collector.

Per impostare le dimensioni iniziale e massima della Generazione Permanente sono previsti rispettivamente i flag  -XX:PermGen e -XX:MaxPermGen.

MetaSpace

A partire dalla versione 8 di Java, lo spazio di memoria MetaSpace sostituisce lo spazio PermGen. La nuova implementazione differisce dal PermGen e adesso questa porzione di heap viene ridimensionata in modo automatico.

Questo evita il problema dell'esaurimento di memoria da parte delle applicazioni a causa della dimensione limitata dello spazio PermGen nell'heap. La memoria MetaSpace può essere pulita tramite la garbage collection e le classi che non vengono più utilizzate possono essere rimosse automaticamente quando il MetaSpace raggiunge la dimensione massima.

Tipi di Garbage Collector nella Java Virtual Machine

La garbage collection rende la memoria Java efficiente perché rimuove gli oggetti non più referenziati dall'heap liberando spazio che diventa disponibile per l'allocazione dei nuovi oggetti.

La Java Virtual Machine dispone di otto tipi diversi di garbage collector. Vediamo di seguito i dettagli per ciascuna tipologia.

Seriale

Si tratta della più semplice implementazione di GC che è stata progettata per piccole applicazioni eseguite su ambienti in modalità mono-thread. Tutti gli eventi di garbage collection sono eseguiti sequenzialmente in un unico thread. La compattazione è eseguita dopo ciascun ciclo di collection.

image-68

Quando viene eseguito, determina un evento "stop the world" (letteralmente "arresta, interrompi il mondo") in corrispondenza del quale l'intera applicazione è posta in pausa. Dato che l'intera applicazione è congelata per la durata della garbage collection, non è consigliato per scenari del mondo reale nel quale sono generalmente richieste latenze ridotte.

L'argomento della JVM utilizzato per impostare il GC Seriale è -XX:+UseSerialGC.

Parallelo

Il collector parallelo è adatto ad applicazioni con data set di dimensioni comprese tra medie e grandi che siano eseguite su di un hardware multiprocessore in modalità multi-thread. Questa è l'implementazione di default per il GC nella JVM ed è anche conosciuto come Throughput Collector.

Sono utilizzati thread multipli per gli eventi minori di garbage collection verso la Generazione Giovane. Un singolo thread gestisce invece gli eventi maggiori di collection sulla Generazione Vecchia.

image-66

Eseguire il GC Parallelo determina comunque un evento "stop the world" e il congelamento dell'applicazione. Dato che è più adatto ad un ambiente multi-thread, può essere utilizzato quando una gran quantità di lavoro deve essere completata e pause lunghe sono ritenute accettabili, ad esempio eseguendo un job batch.

L'argomento della JVM da impostare per l'utilizzo del Garbage Collector Parallelo è  -XX:+UseParallelGC.

Vecchio GC Parallelo

Questa è la versione di default del GC Parallelo a partire dalla versione Java 7u4. Ha le medesime caratteristiche del GC Parallelo tranne per il fatto che sfrutta thread multipli sia per eseguire collection sulla Generazione Giovane che sulla Generazione Vecchia.

L'argomento da passare alla JVM per utilizzare il Garbage Collector Parallelo Vecchio è invece -XX:+UseParallelOldGC.

CMS - Concurrent Mark Sweep (Pulizia e Marcatura Concorrenti)

Anche conosciuto come il collector concorrente con pausa ridotta. Utilizza thread multipli per eventi di garbage collection minore implementando lo stesso algoritmo del Parallelo. La garbage collection maggiore è implementata in multi-thread, analogamente a quanto avveniva con il Vecchio GC Parallelo, ma nel caso del CMS, è eseguita in concorrenza con i processi dell'applicazione in modo da minimizzare le occorrenze di eventi "stop the world".

image-67

A causa di ciò, il collector CMS necessita di maggiori risorse di CPU rispetto agli altri GC. Se nel sistema vi è disponibilità per allocare una quantità maggiore di risorse CPU per ottenere performance migliori, allora il collector CMS sarà una scelta migliore rispetto al collector Parallelo. La compattazione non è invece eseguita dal collector CMS.

L'argomento per la JVM da utilizzare per impostare il GC Concurrent Mark Sweep è invece  -XX:+UseConcMarkSweepGC.

G1 - Garbage First (Prima la spazzatura)

Il G1 era stato pensato per sostituire CMS ed era progettato per applicazioni multi-thread con un ampio spazio di memoria heap (superiore a 4GB) a disposizione. Si tratta di un'implementazione parallela e concorrente come il CMS, ma che funziona diversamente se paragonata ai garbage collector più vecchi.

Nonostante anche G1 condivida l'approccio generazionale, non utilizza regioni separate per le generazioni giovane e vecchia. Invece, ciascuna generazione costituisce un insieme di regioni, le quali consentono il ridimensionamento della generazione giovane in modo flessibile.

L'heap viene partizionato in un insieme di regioni di egual dimensione (da 1MB a 32MB - a seconda della dimensione dell'heap) e utilizza thread multipli per la scansione delle stesse. Una regione potrebbe essere classificata sia come vecchia che come nuova in un momento qualsiasi durante l'esecuzione del programma.

Dopo il completamento della fase di marcatura, G1 è in grado di individuare quali regioni contengano la gran parte degli oggetti da pulire. Se l'utente è interessato a ridurre al minimo i tempi di pausa, G1 può scegliere di rimuovere solo poche regioni. Se, al contrario, l'user non ha criticità legate ai tempi di pausa oppure ha dichiarato un limite di tempo di pausa sufficientemente elevato, G1 potrebbe scegliere di includere un maggior numero di regioni.

Dato che G1 identifica la regione con il maggior numero di oggetti da rimuovere ed esegue la garbage collection proprio a partire da quella regione, viene anche detto Garbage First.

image-88

In aggiunta agli spazi Eden, Sopravvissuti e Old, in G1GC sono previsti altri due tipi di regioni:

  • Enorme (Humongous) - destinato agli oggetti di grandi dimensioni (che richiedono uno spazio superiore al 50% della dimensione dell'heap)
  • Disponibile (Available) - lo spazio inutilizzato o non allocato

Per impostare il garbage collector G1 il flag da passare alla JVM è  -XX:+UseG1GC.

Epsillon

Epsilon è un collector di tipo "do-nothing" (letteralmente "non fare niente") che è stato rilasciato con la JDK 11. Gestisce l'allocazione della memoria ma senza implementare alcun meccanismo di recupero dello spazio. Una volta che lo spazio disponibile per l'heap viene esaurito, la JVM si arresta.

Può essere usato per applicazioni ultrasensibili ai vincoli di latenza, nelle quali gli sviluppatori sono in grado di stabilire esattamente l'impronta di occupazione della memoria (footprint), oppure lavorano ad applicazioni ottimizzate a tal punto da essere quasi prive di oggetti obsoleti (garbage-free). L'utilizzo del GC Epsilon al di fuori di questi scenari è in genere sconsigliato.

Il flag da passare alla JVM per utilizzare il Garbage Collector Epsilon è il seguente -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC.

Shenandoah

Shenandoah è un nuovo GC rilasciato con la JDK 12. Il principale vantaggio di Shenandoah rispetto a G1 è che svolge una parte maggiore del ciclo della sua garbage collection in modo concorrente con i thread dell'applicazione. G1 può liberare le regioni nell'heap solamente quando l'applicazione è posta in pausa, mentre Shenandoah può trasferire gli oggetti in modo concorrente con l'applicazione.

Shenandoah può compattare oggetti vivi, rimuovere gli oggetti obsoleti e restituire RAM al Sistema Operativo quasi immediatamente dopo aver rilevato memoria libera. Come conseguenza al fatto che tutte queste attività sono condotte in concorrenza con l'applicazione, Shenandoah necessita di maggiori risorse CPU.

L'argomento da passare alla JVM per utilizzare il GC Shenandoah è infine il seguente -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC.

ZGC

ZGC è un altro GC che è stato inserito nella JDK 11 ed è stato poi migliorato nella JDK 12. È stato pensato per applicazioni che richiedano bassa latenza (con pause inferiori ai 10 ms) e/o che usino un heap molto esteso (dell'ordine dei Terabyte).

Gli obiettivi primari del ZGC sono latenza contenuta, scalabilità, facilità d'uso. Per raggiungere queste performance, ZGC permette a un'applicazione Java di continuare l'esecuzione mentre vengono eseguite tutte le operazioni di garbage collection. Inoltre, come comportamento di default, ZGC revoca la memoria inutilizzata e la restituisce al sistema operativo.

In questo modo, ZGC introduce un netto miglioramento rispetto ai GC tradizionali e riuscendo a offrire tempi di pausa estremamente ridotti (tipicamente inferiori ai 2 ms).

figure2_600w
Source: oracle.com

Il flag da impostare sulla JVM per attivare ZGC è invece -XX:+UnlockExperimentalVMOptions -XX:+UseZGC.

Nota: con l'introduzione della JDK 15, sia Shenandoah che ZGC sono diventati feature di produzione della JDK dopo essere stati promossi dallo stage sperimentale.

Come scegliere il Garbage Collector più adatto

Quando la tua applicazione non ha stretti vincoli sui tempi di pausa, puoi eseguire l'applicazione lasciando alla JVM la selezione del giusto collector.

Nella maggior parte degli scenari, le impostazioni di base hanno una buona resa. Se necessario, puoi regolare la dimensione dell'heap per migliorare le performance. Se le performance non sono ancora adeguate, puoi impostare il collector secondo i requisiti dell'applicazione:

  • Seriale - Se l'applicazione lavora su dati di dimensione contenuta (approssimativamente fino a 100 MB) e/o sarà eseguita su di un singolo processore senza vincoli sui tempi di pausa
  • Parallelo - Se il picco di performance è la priorità e non ci sono particolari vincoli per quanto riguarda i tempi di pausa e interruzioni di un secondo o maggiori sono ritenute accettabili
  • CMS/G1 - Se il tempo di risposta è più importante del rendimento complessivo e le pause dovute all'intervento del garbage collection devono essere limitate approssimativamente a un secondo
  • ZGC - Se il tempo di risposta è critico, e/o si sta lavorando con un heap molto esteso

Vantaggi della Garbage Collection

I vantaggi dell'azione della garbage collection in Java sono molteplici.

Prima di tutto, rende il codice più semplice. Non ci si deve preoccupare di gestire cicli di assegnamento e revoca della memoria. È sufficiente smettere di referenziare un oggetto nel codice e, a un certo punto, la memoria occupata dallo stesso oggetto verrà recuperata automaticamente.

I programmatori che lavorano con linguaggi privi di garbage collection (quali C e C++), devono implementare esplicitamente la gestione della memoria nel proprio codice.

Rende inoltre Java efficiente in termini di risorse di memoria, in quanto, il garbage collector rimuove gli oggetti non più referenziati dalla regione dell'heap. Ciò determina la creazione di spazio libero di memoria heap per accogliere i nuovi oggetti istanziati dal programma.

Mentre alcuni programmatori criticano la garbage collection in favore di una gestione più manuale della memoria, la garbage collection è diventata un componente standard per molti altri popolari linguaggi di programmazione.

Per scenari in cui la garbage collection introduce ricadute negative sulle performance, Java consente molte opzioni per impostare il garbage collector più accuratamente e in modo da migliorare la sua efficienza.

Buone Pratiche

Evitare Chiamate Manuali

Oltre al funzionamento di base della garbage collection, uno dei punti fondamentali da capire sulla garbage collection in ambiente Java riguarda il fatto che essa segue un comportamento non-deterministico. Questo si traduce nell'impossibilità di predire quando la garbage collection si verificherà durante l'esecuzione.

È possibile includere un suggerimento nel codice per eseguire il garbage collector, attraverso istruzioni quali chiamate ai metodi System.gc() o Runtime.gc(), ma è importante ricordare che questi non offrono alcuna garanzia che il GC verrà effettivamente richiamato in quel punto del programma.

Utilizzare strumenti per l'analisi

Se non si dispone di sufficiente memoria per eseguire l'applicazione, si sperimentano rallentamenti, lunghi tempi di pausa per garbage collection, frequenti eventi di "stop the world" e infine anche errori di "out of memory" (imprevisto esaurimento della memoria). Questo può indicare uno spazio di heap troppo ridotto, ma può anche significare la presenza di memory leak nell'applicazione.

Si può ottenere aiuto da un tool di monitoraggio come jstat oppure Java Flight Recorder per comprendere se l'occupazione dell'heap cresca indefinitamente, comportamento che è tipicamente un sintomo di un bug nel codice.

Le impostazioni di default sono valide

Quando si sta eseguendo una piccola applicazione Java in configurazione standalone, molto probabilmente non si avvertirà la necessità di alcuna impostazione personalizzata della garbage collection. Le impostazioni di default dovrebbero infatti funzionare correttamente nella maggior parte dei casi.

Flag JVM per cambiare le impostazioni

L'approccio più corretto per impostare la garbage collection in Java è quello di inserire i flag nella riga di comando della JVM. I Flag possono essere utilizzati per scegliere quale garbage collector applicare (Seriale, G1 e così via), la dimensione iniziale e quella massima dell'heap, la dimensione delle sezioni dell'heap (ad esempio per gli spazi della Generazione Giovane e della Generazione Vecchia) e anche altri parametri.

Scegliere il giusto collector

La natura dell'applicazione da ottimizzare è una buona guida iniziale per effettuare le impostazioni corrette. Ad esempio, il garbage collector parallelo è efficiente ma causa frequenti eventi "stop the world", rendendolo più adatto a processi backend nei quali, pause più lunghe per la garbage collection sono accettabili.

D'altro canto, il garbage collector CMS è progettato per minimizzare le pause, rendendolo ideale per applicazioni web nelle quali i tempi di risposta sono un fattore critico.

Conclusione

In questo articolo, abbiamo discusso la Garbage Collection in Java, il suo funzionamento e i diversi tipi di Garbage Collector.

Per quanto riguarda le applicazioni più semplici, la garbage collection non rientra tra i fattori principali che un programmatore deve considerare durante lo sviluppo. Tuttavia, per i programmatori che vogliono accrescere le proprie abilità in Java, è importante comprendere il funzionamento della garbage collection.

Si tratta inoltre di un argomento popolare nei colloqui tecnici, sia a livelli junior che senior, per ruoli backend.

Grazie per essere rimasto con me così a lungo. Spero ti sia piaciuto l'articolo. Puoi connetterti con me su LinkedIn dove scrivo post sulla tecnologia e sulla vita. Dai anche un'occhiata a qualche altro mio articolo e al mio canale YouTube. Buona lettura. 🙂