In Java la gestione della memoria è effettuata tramite la raccolta automatica dei rifiuti (Garbage Collector, GC). La JVM traccia automaticamente gli oggetti in memoria: se non ci sono più riferimenti a un oggetto, esso diventa inaccessibile e può essere eliminato.
Il GC è composto da diverse fasi, come Mark, Sweep e a volte Compact. Nella fase Mark, gli oggetti raggiungibili dalle radici (GC roots) vengono contrassegnati come vivi. Successivamente, inizia la fase Sweep: gli oggetti non utilizzati vengono eliminati. A volte viene eseguita la fase Compact, ovvero la deframmentazione della memoria.
Ci sono diversi tipi di GC: Serial, Parallel, CMS, G1. È consigliabile scegliere in base al tipo di carico sull'applicazione.
List<byte[]> dataList = new ArrayList<>(); while(true) { dataList.add(new byte[1024*1024]); // Gli oggetti rimangono accessibili }
In questo esempio, gli oggetti non diventeranno mai inaccessibili, il GC non li eliminerà e si verificherà un OutOfMemoryError.
Se un oggetto non ha più riferimenti, quando si garantisce la chiamata del suo finalizzatore (finalize())?
Risposta: La chiamata al metodo finalize() non è garantita affatto! Può non essere mai chiamato o essere chiamato con ritardo. Non ci si può fare affidamento su finalize() per liberare risorse.
@Override protected void finalize() throws Throwable { // Potrebbe non essere eseguito! }
Storia
In un grande sistema di e-commerce, gli sviluppatori speravano di automatizzare la pulizia dei file temporanei utilizzando il metodo finalize(), ma a causa della rara chiamata di questo metodo, la cache è stata riempita di file temporanei, causando mancanza di spazio su disco e downtime.
Storia
In un'API REST ad alta capacità, è stata accidentalmente mantenuta una cache interna di riferimenti a grandi oggetti, impedendo il rilascio della memoria. Dopo il crash dell'applicazione con OutOfMemoryError, l'analisi ha rivelato un errore nel metodo di caching.
Storia
I finalizzatori sono stati utilizzati per liberare connessioni di rete, considerandoli un "modo affidabile". Questo portava a una situazione in cui le connessioni rimanevano aperte nel sistema per un tempo indefinito, causando blocchi e esaurimento del pool di connessioni.