Tag: java, jvm, memory, garbage-collector, performance, backend
Livello: intermedio
TL;DR: Panoramica essenziale dell'architettura JVM, gestione della memoria tra Heap e Stack, ciclo di vita degli oggetti e Garbage Collection.
La JVM esegue bytecode Java rendendo il codice indipendente dall'hardware sottostante.
Lo Stack memorizza frame dei metodi, variabili locali e riferimenti a oggetti per ogni thread.
L'Heap memorizza tutte le istanze degli oggetti ed e' condiviso tra tutti i thread.
La memoria Heap e' suddivisa principalmente in Young Generation e Old (Tenured) Generation.
La Young Generation include Eden Space e due Survivor Space (S0, S1).
La Metaspace risiede nella memoria nativa e contiene i metadati delle classi caricate.
Il ClassLoader carica, collega e inizializza i file di classe a runtime.
Il JIT Compiler compila a runtime il bytecode frequentemente eseguito in codice macchina nativo.
Il Garbage Collector individua gli oggetti non piu' raggiungibili e libera la memoria associata.
Stop-the-world indica la pausa di tutti i thread applicativi durante alcune fasi del GC.
Q: Qual e' la differenza principale tra Stack e Heap?R: Lo Stack alloca memoria per thread con frame di metodo e variabili primitive locali in modo rapido e ordinato LIFO. L'Heap gestisce l'allocazione dinamica di tutti gli oggetti ed e' sottoposto al ciclo del Garbage Collector.
Q: Come funziona il passaggio di parametri in Java?R: Java e' strettamente pass-by-value: per i tipi primitivi viene copiata la variabile, per gli oggetti viene copiato il valore del riferimento.
Q: Quando si verifica un OutOfMemoryError sull'Heap?R: Succede quando l'applicazione tenta di allocare un oggetto nell'Heap ma non c'e' spazio sufficiente e il GC non riesce a liberarne abbastanza.
Q: Come opera la divisione generazionale nell'Heap?R: I nuovi oggetti nascono in Eden; i superstiti ai Minor GC passano nei Survivor e infine, raggiunta una soglia di eta', vengono promossi nella Old Generation.
Trattenere riferimenti in collezioni statiche o listener non rimossi causando memory leak silenziosi.
Confondere OutOfMemoryError relativo all'Heap con quello relativo a Metaspace o thread stack.
Invocare esplicitamente System.gc(), introducendo pause non previste e vanificando l'euristica interna del GC.
Sovradimensionare l'Heap provocando pause di Stop-the-World molto piu' lunghe durante le Major GC.
Ignorare la differenza tra memoria heap e resident set size (RSS) del processo a livello di sistema operativo.
# Impostazione dimensione minima e massima dell'Heap
java -Xms2g -Xmx4g -jar app.jar
Bash
# Abilitazione del Garbage Collector G1 e logging del GC
java -XX:+UseG1GC -Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=100M -jar app.jar
Bash
# Ispezione dei consumi della memoria con jstat
jstat -gcutil <pid> 1000
Bash
# Generazione dell'heap dump in caso di OutOfMemoryError
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/heapdump.hprof -jar app.jar
[ ] Differenze tra GC moderni: G1, ZGC e Shenandoah
[ ] Monitoraggio tramite jcmd, jstack e jmap
[ ] Escape analysis e allocazione scalare
[x] Struttura e suddivisione dell'Heap tra Young e Old Generation