Anatomia della memoria
Esistono due tipi di memoria:
- heap
- stack
Gli oggetti vengono gestiti dinamicamente e la gestione dinamica consiste nell’allocare gli oggetti nella heap.
Il compilatore mette nello stack delle strutture dati dove le variabile locali dei metodi saranno scritte nei frame.
Quindi la gestione dello stack viene prestabilita compilando il codice, la gestione della heap è una questione dinamica.
La parola chiave static
Un campo static esiste in una sola locazione di memoria, allocata prima di qualsiasi oggetto della classe in una zione speciale di memoria nativa chiamata MetaSpace.
E’ condiviso da tutte le sue istanze e quindi è relativo all’intera classe.
Per ogni campo non statico esiste una locazione di memoria per ogni oggetto, allocata a seguito dell’istruzione new
Esempio classe Tornello
public class Tornello
{
static private int passaggi;
public void passa() {passaggi++;}
public static int getPassaggi() {return passaggi;}
public static void main (String[] args)
{
Tornello t1 = new Tornello();
t1.passa();
Tornello t2 = new Tornello();
for (int k = 0; k < 10; k++) t2.passa();
int g;
String s = null;
}
}
Heap, stack e metaspace
- Nello stack si rappresentano i frame di attivazione (chiamate ai metodi) e le variabili locali
- Nella heap si rappresenta l’allocazione degli oggetti
- Nel metaspace si rappresenta l’allocazione dei campi
Drawing 2024-08-09 17.53.18.excalidraw
Quando compilo il codice la JVM
usa un modulo chiamato class loader che controlla innanzitutto la presenza del metodo main
.
Quindi controlla se ci sono campi statici. Nell’esempio tornello c’è un campo statico (static private int passaggi;
).
Quindi passaggi verrĂ allocato nel metaspace
Dopo aver controllato tutti i campi statici, la JVM
passa al metodo main
.
Il main è un metodo che ha sempre un vettore di stringhe.
Se ci sono degli argomenti, viene costruito un vettore i cui elementi sono le stringhe che sono passate come argomento di programma.
Se non ci sono stringhe, il vettore sarĂ vuoto.
La JVM
crea il frame di attivazione del metodo main
.
Ogni volta che viene chiamato un metodo, tutte le variabili locali che corrispondono ai parametri attuali, vengono copiati.
Tutto ciò che viene passato come metodo viene copiato e allocato nello stack, quindi args
diventa una variabile
Ora siamo nella riga
Tornello t1 = new Tornello();
Viene chiamato il costruttore di default e viene allocato nello stack
E’ una dichiarazione di t1
di tipo Tornello.
Costruisce l’oggetto e lo alloca nella heap.
Ora ho la chiamata del metodo passa()
a partire dal riferimento t1
.
t1.passa();
Avremo quindi un nuovo frame che si posizionerĂ sopra il main
.
La JVM
crea un’altra area di memoria per rappresentare ciò che deve fare nell’eseguire il metodo passa()
.
Nel passaggio successivo t1.passa()
viene distrutto e al suo posso viene scritto il frame Tornello()
Tornello t2 = new t2 = new Tornello();
Stiamo chiamando un metodo, quello di costruzione del tornello, il costruttore di default.
Quando chiamo il costruttore la JVM
inizia ad allocare il progetto, controlla se ci sono campi e istruzioni e alloca il progetto nella heap.
Tornello t2 = new Tornello();
Viene allocato t2
che è una variabile locale.
for (int k = 0; k < 10; k++) t2.passa();
Con quest’istruzione intendiamo chiamare passa a partire dall’oggetto t2
.
k = 0
diventa temporaneamente una variabile locale di tipo intero.
Viene allocata nel frame main
Viene richiamato il metodo passa()
.
Il frame t2.passa()
viene distrutto
K diventa 1 e richiamo di nuovo passa()
Continuo a chiamare passa()
finché k>=10
K sparisce dal frame alla fine del ciclo for
int g;
viene creata una variabile primitiva g
che avrĂ come valore undef
.
String s = null;
Viene creata una variabile locale s
di tipo String
che ha come valore null
.
+