JVM — Cheat Sheet

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.

Concetti chiave

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.

Domande tipiche e risposte pronte

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.

Insidie e errori comuni

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.

Snippet e comandi

# 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

Da ripassare

[ ] 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

Jarvis Interviews
Jarvis