Gestione della memoria in Java

โšก Riepilogo intelligente

Gestione della memoria in Java spiega come la JVM divide la memoria di runtime tra Stack, Heap, Codee le regioni statiche, il modo in cui i riferimenti agli oggetti fluiscono durante le chiamate ai metodi e come il Garbage Collector recupera gli oggetti irraggiungibili in modo che le applicazioni rimangano stabili e prive di perdite di memoria.

  • ๐Ÿง  Disposizione della memoria JVM: Java La memoria รจ suddivisa in Heap per gli oggetti, Stack per i frame dei metodi e le variabili locali, oltre ad aree dedicate al bytecode e ai dati statici.
  • ๐Ÿ“š Stack contro Heap: Gli stack frame seguono l'ordine Last-In-First-Out (LIFO) e memorizzano primitive e riferimenti, mentre l'heap contiene tutti gli oggetti creati con l'operatore new.
  • โ™ป๏ธ Raccolta dei rifiuti: Il netturbino tracks raggiungibilitร  tramite catene di riferimenti e recupera automaticamente la memoria dagli oggetti irraggiungibili da qualsiasi thread.
  • โœ… Collezionisti moderni: I carichi di lavoro di produzione si affidano sempre piรน a G1, ZGC e Shenandoah per la raccolta a bassa interruzione su heap multigigabyte.
  • ๐Ÿงช Esempio pratico: Una guida passo passo per studenti mostra quando i riferimenti diventano idonei per la raccolta e come l'annullamento dei riferimenti libera l'oggetto sottostante.

Gestione della memoria in Java

Cos'รจ la memoria stack in Java?

Memoria dello stack in Java Lo Stack รจ la regione di memoria della JVM che memorizza i frame dei metodi, le variabili locali e le variabili di riferimento per ogni thread. Lo Stack viene sempre acceduto secondo l'ordine Last-In-First-Out (LIFO), quindi il metodo invocato piรน di recente si trova in cima e le sue variabili locali vengono inserite e rimosse insieme al frame.

Ogni filo nel Java La macchina virtuale riceve il proprio Stack, che per impostazione predefinita mantiene le invocazioni dei metodi isolate e thread-safe. I tipi primitivi come int, boolean e double risiedono direttamente all'interno del frame, mentre i riferimenti agli oggetti memorizzati nello Stack puntano agli oggetti allocati nell'Heap.

Che cos'รจ la memoria Heap in Java?

La memoria heap รจ la regione JVM condivisa che contiene ogni Java Oggetto e array creati con l'operatore new, insieme a tutte le variabili di riferimento che appartengono a tali oggetti come campi di istanza. A differenza dello Stack, l'Heap รจ condiviso tra tutti i thread, motivo per cui l'accesso agli oggetti spesso richiede la sincronizzazione.

L'heap รจ l'area gestita dal Garbage Collector. Le moderne JVM HotSpot dividono l'heap in una Young Generation per gli oggetti di breve durata e una Old Generation per gli oggetti di lunga durata, con i metadati delle classi memorizzati in una regione nativa separata chiamata Metaspace.

Allocazione memoria in Java

Allocazione memoria in Java รˆ il processo mediante il quale la JVM riserva regioni di memoria virtuale per variabili e istanze di classi e strutture durante l'esecuzione del programma. La memoria non viene allocata a un oggetto al momento della dichiarazione; viene creato solo un riferimento. L'effettiva allocazione dell'oggetto avviene tramite l'operatore `new`, quindi ogni oggetto risiede nell'heap.

Migliori Java L'allocazione della memoria รจ suddivisa nelle seguenti sezioni:

  1. Heap
  2. pila
  3. Code
  4. statica

Questa suddivisione della memoria รจ necessaria per una gestione efficace del tempo di esecuzione.

  • Migliori Code la sezione contiene il tuo compilato codice a byte.
  • Migliori pila reparto Negozio metodi, variabili locali e variabili di riferimento.
  • Migliori Heap sezione contiene oggetti e possono anche contenere variabili di riferimento memorizzate come campi di istanza.
  • Migliori statica la sezione contiene dati statici e metodi statici condiviso tra tutte le istanze.

Differenza tra variabile locale e di istanza

Capire dove risiede ogni tipo di variabile nella JVM aiuta a spiegare il comportamento di Stack e Heap. variabile di istanza รจ dichiarato all'interno di una classe ma al di fuori di qualsiasi metodoe risiede nell'Heap come parte del suo oggetto.

class Student{ 
int num; // num is instance variable 
public void showData{}

A variabile locale รจ dichiarato all'interno di un metodo, inclusi gli argomenti del metodoe risiede nello Stack all'interno del frame attivo.

public void sum(int a){

int x = a + 3;

// a, x are local variables

}

Differenza tra Stack e Heap

Lo Stack e l'Heap risolvono problemi diversi all'interno della JVM. Lo Stack fornisce a ciascun thread un'allocazione rapida e deterministica per i dati di breve durata legati all'ambito del metodo, mentre l'Heap offre una regione condivisa per gli oggetti di lunga durata a cui qualsiasi thread puรฒ fare riferimento. Il breve video qui sotto riassume le differenze prima della spiegazione dettagliata che segue.

Clicchi Qui. se il video non รจ accessibile

Per capire come Stack e Heap collaborano, consideriamo un metodo main che chiama il metodo m1.

public void m1{
int x = 20;
}

Nello stack della JVM viene creato un frame per il metodo m1.

Java Stack e Heap

La variabile x in m1 viene creata anche nel frame per m1 sullo Stack, come mostrato nell'immagine sottostante.

Java Stack e Heap

Il metodo m1 chiama quindi il metodo m2. Nello stack, viene creato un nuovo frame per m2 sopra il frame per m1.

Java Stack e Heap

Java Stack e Heap

Le variabili locali b e c vengono create anche all'interno del frame per m2 sullo Stack.

public void m2(int b){
boolean c;
}

Successivamente, m2 chiama il metodo m3. Anche in questo caso, viene creato un frame per m3 in cima allo Stack, come mostrato di seguito.

Java Stack e Heap

Java Stack e Heap

Ora diciamo che il metodo m3 crea un oggetto per la classe Account, che ha due variabili di istanza int p e int q.

class Account {
    int p;
    int q;
}

Ecco il codice per il metodo m3.

public void m3(){
    Account ref = new Account();
    // more code
}

L'istruzione new Account() crea un oggetto Account nell'heap.

Java Stack e Heap

La variabile di riferimento ref viene creata sullo Stack all'interno del frame per m3.

Java Stack e Heap

L'operatore di assegnazione fa sรฌ che la variabile di riferimento punti all'oggetto sull'heap.

Java Stack e Heap

Una volta completata l'esecuzione del metodo, il controllo ritorna al metodo chiamante, che in questo caso รจ il metodo m2.

Java Stack e Heap

Il frame per il metodo m3 viene svuotato dallo stack.

Java Stack e Heap

Poichรฉ la variabile di riferimento non punta piรน all'oggetto nell'heap, tale oggetto diventa idoneo per la raccolta dei rifiuti (garbage collection).

Java Stack e Heap

Una volta terminato il metodo m2, questo viene rimosso dallo stack e tutte le sue variabili diventano non disponibili. Lo stesso accade per il metodo m1, e infine il controllo ritorna al metodo main.

Cosa succede se un oggetto contiene un altro riferimento come variabile di istanza?

public static void main(String args[]) {
    A parent = new A();
    // more code
}
class A {
    B child = new B();
    int e;
}
class B {
    int c;
    int d;
}

In questo caso, la variabile di riferimento child risiede nell'heap come parte dell'oggetto A e, a sua volta, punta al proprio oggetto B, come mostrato di seguito.

Java Stack e Heap

In cosa consiste la Garbage Collection Java?

Raccolta rifiuti a Java Il Garbage Collector รจ il processo tramite il quale la JVM gestisce automaticamente la memoria. Il Garbage Collector individua gli oggetti che non sono piรน raggiungibili da alcun riferimento attivo e ne recupera la memoria. L'allocazione dinamica della memoria avviene tramite l'operatore `new` e la memoria rimane allocata finchรฉ il programma non detiene piรน alcun riferimento all'oggetto.

Quando non rimangono riferimenti, l'oggetto viene considerato non piรน necessario e la memoria che occupa puรฒ essere recuperata. Non c'รจ bisogno esplicito di distruggere un oggetto perchรฉ Java gestisce automaticamente la deallocazione tramite il Garbage Collector.

La tecnica alla base di questo รจ nota come Raccolta dei rifiutiI programmi che non riescono a rilasciare la memoria finiscono per bloccarsi quando non rimane piรน nulla da allocare. Si dice che tali programmi abbiano perdite di memoria. Raccolta rifiuti a Java si avvia automaticamente per tutta la durata del programma, il che elimina l'onere della deallocazione manuale e riduce il rischio di fughe di dati.

In C, al contrario, il programmatore รจ responsabile del rilascio della memoria allocata dinamicamente tramite la funzione free(). รˆ qui che Java La gestione della memoria offre un notevole vantaggio.

Le moderne JVM HotSpot includono diversi Garbage Collector ottimizzati per diversi carichi di lavoro. Il Garbage Collector G1 predefinito punta a un throughput bilanciato e a tempi di pausa su heap multi-gigabyte. ZGC e Shenandoah puntano a pause inferiori al millisecondo su heap molto grandi, il che li rendetractivo per i servizi sensibili alla latenza. Scegliere il collettore giusto e ottimizzare le dimensioni di Young Generation, Old Generation e Metaspace รจ una competenza fondamentale per Java lavoro di performance.

Nota: Tutti gli oggetti vengono creati nella sezione Heap della memoria, che รจ la regione gestita dal Garbage Collector.

Esempio: Per apprendere il meccanismo di raccolta dei rifiuti in Java

Questa procedura illustra quando i riferimenti diventano idonei per la raccolta automatica all'interno di un semplice programma.

Passo 1) Copiare il codice seguente in un editor.

class Student{
int a;
int b;
  public void setData(int c,int d){
    a=c;
    b=d;
  }
  public void showData(){
    System.out.println("Value of a = "+a);
    System.out.println("Value of b = "+b);
  }
  public static void main(String args[]){
    Student s1 = new Student();
    Student s2 = new Student();
    s1.setData(1,2);
    s2.setData(3,4);
    s1.showData();
    s2.showData();
    //Student s3;
    //s3=s2;
    //s3.showData();
    //s2=null;
    //s3.showData();
    //s3=null;
    //s3.showData();
  }
}

Passo 2) Salva, compila ed esegui il codice. Come mostrato nel diagramma, vengono creati due oggetti e due variabili di riferimento.

 Meccanismo di raccolta dei rifiuti

Passo 3) Rimuovi il commento dalle righe 20, 21 e 22. Salva, compila ed esegui il codice.

Passo 4) Come illustrato nel diagramma sottostante, ora due variabili di riferimento puntano allo stesso oggetto.

Meccanismo di raccolta dei rifiuti

Passo 5) Rimuovi il commento dalle righe 23 e 24. Salva, compila ed esegui il codice.

Passo 6) Come mostrato di seguito, s2 diventa nullo, ma s3 punta ancora all'oggetto, quindi l'oggetto non รจ ancora idoneo per la Garbage Collection.

 Meccanismo di raccolta dei rifiuti

Passo 7) Rimuovi il commento dalle righe 25 e 26. Salva, compila ed esegui il codice.

Passo 8) A questo punto, non ci sono piรน riferimenti all'oggetto, quindi diventa idoneo per il Garbage Collector. Il Garbage Collector lo rimuove dalla memoria e non c'รจ modo di recuperarlo.

 Impara il Garbage Collector

Come eliminare un oggetto in Java?

Java non fornisce un operatore di eliminazione manuale, quindi l'approccio standard รจ quello di rimuovere ogni riferimento all'oggetto in modo che il Garbage Collector possa recuperarlo.

1) Per rendere un oggetto idoneo alla Garbage Collection, assegna a null il valore a tutte le variabili di riferimento che puntano ad esso.

2) I tipi primitivi non sono oggetti, quindi non possono essere assegnati a null. La loro memoria viene recuperata automaticamente quando il frame Stack circostante viene estratto.

Come eliminare un oggetto in Java

DOMANDE FREQUENTI

Lo Stack memorizza i frame dei metodi, le variabili locali e i riferimenti in ordine Last-In-First-Out per ogni thread. L'Heap memorizza tutti gli oggetti creati con `new` ed รจ condiviso tra i thread, motivo per cui viene gestito dal Garbage Collector.

Il Garbage Collector percorre le catene di riferimenti a partire dalle radici del GC, come ad esempio i thread attivi e i campi statici. Qualsiasi oggetto irraggiungibile da una radice viene considerato irraggiungibile e puรฒ essere recuperato durante il successivo ciclo di raccolta.

La Young Generation contiene oggetti di breve durata negli spazi Eden e Survivor. La Old Generation contiene oggetti che sopravvivono a piรน raccolte. Metaspace รจ una regione di memoria nativa che memorizza i metadati di classe e ha sostituito il vecchio PermGen in Java 8.

G1 รจ l'impostazione predefinita e si adatta alla maggior parte dei carichi di lavoro su heap multi-gigabyte. ZGC e Shenandoah puntano a pause inferiori al millisecondo su heap molto grandi e sono buone scelte per i servizi sensibili alla latenza in esecuzione su Java 17 o successivo.

Imposta a null ogni riferimento che punta all'oggetto, oppure lascia che i riferimenti escano dall'ambito quando i relativi frame dello Stack vengono rimossi. Una volta che nessun riferimento attivo raggiunge l'oggetto, il Garbage Collector recupera la memoria Heap.

Sรฌ. Gli strumenti di ottimizzazione basati sull'IA analizzano i log del garbage collector, i tassi di allocazione e gli istogrammi delle pause per consigliare le dimensioni dell'heap, i flag del garbage collector e i rapporti della Young Generation. Riducono i tempi di feedback rispetto all'ottimizzazione manuale, soprattutto per i carichi di lavoro G1, ZGC e Shenandoah.

Le piattaforme di osservabilitร  basate sull'intelligenza artificiale acquisiscono dump della memoria e metriche in tempo reale, quindi raggruppano i grafici degli oggetti conservati per segnalare classi e catene di riferimenti sospette. Questo permette di individuare potenziali perdite di memoria piรน rapidamente rispetto all'analisi manuale della memoria e aiuta i team a identificare la causa principale in produzione.

Riassumi questo post con: