Condivisione della tecnologia

Relativo alla JVM

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Architettura JDK

Funzionalità multipiattaforma del linguaggio Java

Struttura complessiva della JVM e modello di memoria

Impostazioni dei parametri di memoria JVM

Il formato di impostazione dei parametri JVM del programma Spring Boot (l'avvio di Tomcat viene aggiunto direttamente al file catalina.sh nella directory bin):

java -Xms2048M -Xmx2048M -Xmn1024M -Xss512K -XX:DimensioneMetaspazio=256M -XX:DimensioneMassimaMetaspazio=256M -jar microservizio-eureka-server.jar

Insomma:

Minore è l'impostazione -Xss, minore è il valore di conteggio, il che significa che è possibile allocare meno stack frame in uno stack di thread, ma il numero di thread che possono essere aperti sarà maggiore per la JVM nel suo insieme.

-Xss: dimensione dello stack di ciascun thread, predefinito 1M

-Xms: imposta la dimensione disponibile iniziale dell'heap, il valore predefinito è 1/64 di memoria fisica

-Xmx: imposta la dimensione massima disponibile dell'heap, il valore predefinito è 1/4 della memoria fisica

-Xmn: dimensione di nuova generazione

-XX:NewRatio: il valore predefinito 2 significa che la nuova generazione rappresenta 1/2 della vecchia generazione e 1/3 dell'intera memoria heap.

-XX:SurvivorRatio: Il valore predefinito 8 significa che un'area sopravvissuta occupa 1/8 della memoria Eden, ovvero 1/10 della memoria di nuova generazione.

Sono presenti due parametri JVM relativi al metaspazio: -XX:MetaspaceSize=N e -XX:MaxMetaspaceSize=N

-XX: MaxMetaspaceSize: imposta la dimensione massima del metaspazio. Il valore predefinito è -1, il che significa che non esiste alcun limite oppure è limitato solo dalla dimensione della memoria locale.

-XX: MetaspaceSize: specifica la soglia iniziale per il metaspace per attivare Fullgc (non esiste una dimensione iniziale fissa per il metaspace), in byte Il valore predefinito è circa 21M. Quando viene raggiunto questo valore, verrà attivato il gc completo per lo scaricamento del tipo il raccoglitore regolerà questo valore: Se viene rilasciata una grande quantità di spazio, ridurre il valore in modo appropriato; se viene rilasciata una piccola quantità di spazio, aumentare il valore in modo appropriato senza superare -XX:MaxMetaspaceSize (se impostato). Questo ha un significato diverso dal parametro -XX:PermSize nelle versioni precedenti di jdk. -XX:PermSize rappresenta la capacità iniziale della generazione permanente.

Poiché il ridimensionamento del metaspazio richiede GC completo, si tratta di un'operazione molto costosa. Se si verifica un numero elevato di GC completi all'avvio dell'applicazione, in genere è dovuto al ridimensionamento della generazione permanente o del metaspazio consigliato Nei parametri JVM, imposta MetaspaceSize e MaxMetaspaceSize sullo stesso valore e impostali su un valore maggiore rispetto al valore iniziale. Per una macchina con memoria fisica 8G, generalmente imposto entrambi i valori su 256M.

Creazione di oggetti

Intestazione dell'oggetto

Nella macchina virtuale HotSpot, il layout degli oggetti archiviati in memoria può essere suddiviso in tre aree: intestazione dell'oggetto (Header), dati dell'istanza (Instance Data) e riempimento dell'allineamento (Padding). L'intestazione dell'oggetto della macchina virtuale HotSpot include due parti di informazioni. La prima parte viene utilizzata per archiviare i dati di runtime dell'oggetto stesso, come codice hash (HashCode), età di generazione GC, flag di stato del blocco, blocco mantenuto dal thread. , ID thread distorto, timestamp preferiti, ecc. L'altra parte dell'intestazione dell'oggetto è il puntatore del tipo, ovvero il puntatore dell'oggetto ai metadati della sua classe. La macchina virtuale utilizza questo puntatore per determinare di quale classe l'oggetto è un'istanza.

Intestazione dell'oggetto a 32 bit

Intestazione dell'oggetto a 64 bit

compressione del puntatore

1. L'utilizzo di puntatori a 32 bit (l'archiviazione effettiva utilizza 64 bit) in HotSpot su una piattaforma a 64 bit utilizzerà circa 1,5 volte più memoria. L'utilizzo di puntatori più grandi per spostare i dati tra la memoria principale e la cache richiede allo stesso tempo più larghezza di banda tempo, anche GC sarà sotto maggiore pressione

2. Per ridurre il consumo di memoria su piattaforme a 64 bit, abilitare la funzione di compressione del puntatore

3. In jvm, gli indirizzi a 32 bit supportano fino a 4G di memoria (2 alla 32a potenza), che può essere ottimizzata comprimendo e codificando il puntatore dell'oggetto quando è archiviato nella memoria heap e quindi decodificandolo dopo averlo preso al registro della CPU (il puntatore dell'oggetto è nell'heap. È 32 bit, 35 bit nel registro, 2 alla 35a potenza = 32G), consentendo a jvm di supportare configurazioni di memoria più grandi (inferiori o uguali a 32G) utilizzando solo indirizzi a 32 bit

4. Quando la memoria heap è inferiore a 4G, non è necessario abilitare la compressione del puntatore. JVM rimuoverà direttamente l'indirizzo alto a 32 bit, ovvero utilizzerà lo spazio degli indirizzi virtuali basso.

5. Quando la memoria heap è maggiore di 32G, il puntatore di compressione diventerà non valido e 64 bit (ovvero 8 byte) saranno costretti a indirizzare l'oggetto Java. Ciò causerà il problema di 1, quindi è meglio non averlo la memoria heap è maggiore di 32G.

Analisi dell'escape e sostituzione del titolo dell'allocazione sullo stack degli oggetti

In questo caso, la JVM può ottimizzare la posizione di allocazione della memoria dell'oggetto attivando il parametro di analisi di escape (-XX:+DoEscapeAnalysis), in modo che venga allocato prima nello stack tramite sostituzione scalare (allocazione sullo stack Escape). l'analisi è attivata per impostazione predefinita dopo JDK7 Se si desidera disattivare l'utilizzo dei parametri (-XX:-DoEscapeAnalysis)

Sostituzione scalare: quando viene determinato tramite l'analisi di escape che non sarà possibile accedere all'oggetto esternamente e che l'oggetto può essere ulteriormente scomposto, la JVM non creerà l'oggetto, ma scomporrà le variabili membro dell'oggetto in diverse variabili membro utilizzate da questo metodo e sostituirli. Queste variabili membro alternative allocano spazio sullo stack frame o sul registro, in modo che non vi sia un'allocazione di memoria insufficiente per l'oggetto perché non c'è uno spazio contiguo di grandi dimensioni. Abilita i parametri di sostituzione scalare (-XX:+EliminateAllocations), che è abilitato per impostazione predefinita dopo JDK7.

Quantità scalari e aggregate: uno scalare è una quantità che non può essere ulteriormente scomposta e il tipo di dati di base di JAVA è uno scalare (come int, long e altri tipi di dati di base e tipi di riferimento, ecc.). è una quantità che può essere ulteriormente decomposta e questa quantità è chiamata quantità di polimerizzazione. In JAVA, gli oggetti sono aggregati che possono essere ulteriormente scomposti.

Gli oggetti di grandi dimensioni entrano direttamente nella vecchia generazione

Gli oggetti di grandi dimensioni sono oggetti che richiedono una grande quantità di spazio di memoria continua (come stringhe e matrici). Il parametro JVM -XX:PretenureSizeThreshold può impostare la dimensione di oggetti di grandi dimensioni. Se l'oggetto supera la dimensione impostata, entrerà direttamente nella vecchia generazione e non entrerà nella giovane generazione. Questo parametro è valido solo con i due raccoglitori Serial e ParNew.

Ad esempio, imposta i parametri JVM: -XX:PretenureSizeThreshold=1000000 (l'unità è byte) -XX:+UseSerialGC Se esegui il primo programma sopra, scoprirai che l'oggetto di grandi dimensioni entra direttamente nella vecchia generazione.

Perché deve essere così?

Per evitare operazioni di copia durante l'allocazione di memoria per oggetti di grandi dimensioni e ridurre l'efficienza.

Giudizio dinamico dell'età dell'oggetto

Nell'area Sopravvissuto in cui è attualmente posizionato l'oggetto (una delle aree, l'area s in cui è posizionato l'oggetto), la dimensione totale di un lotto di oggetti è maggiore del 50% della dimensione della memoria di questa area Sopravvissuto (-XX :TargetSurvivorRatio può essere specificato), quindi in questo momento è maggiore di Gli oggetti che sono uguali all'età massima di questo lotto di oggetti possono entrare direttamente nella vecchia età. Ad esempio, c'è un lotto di oggetti nell'area Sopravvissuto la somma di più oggetti di età con età 1 + età 2 + età n supera il 50% dell'area Sopravvissuto. In questo momento, tutti gli oggetti con età n (inclusa) e superiore verranno inseriti nella vecchia generazione. Questa regola infatti spera che gli oggetti che possono sopravvivere a lungo entrino nella vecchiaia il prima possibile. Il meccanismo di giudizio dinamico dell'età dell'oggetto viene generalmente attivato dopo il gc minore.

Meccanismo di garanzia dell’assegnazione degli spazi di vecchia generazione

Prima di ogni gc minore nella generazione giovane, la JVM calcolerà lo spazio libero rimanente nella vecchia generazione.

Se lo spazio disponibile è inferiore alla somma delle dimensioni di tutti gli oggetti esistenti nella giovane generazione (compresi gli oggetti spazzatura)

Verificherà se il parametro "-XX:-HandlePromotionFailure" (impostato per impostazione predefinita in jdk1.8) è impostato.

Se questo parametro è presente, controllerà se la dimensione di memoria disponibile nella vecchia generazione è maggiore della dimensione media degli oggetti entrati nella vecchia generazione dopo ogni precedente gc minore.

Se il risultato del passaggio precedente è inferiore a o i parametri menzionati prima non sono impostati, verrà attivato un gc completo e la vecchia generazione e la nuova generazione verranno raccolte insieme se non c'è ancora spazio sufficiente per archiviare la nuova oggetti dopo il riciclaggio, si verificherà "OOM".

Naturalmente, se la dimensione degli oggetti sopravvissuti rimanenti che devono essere spostati nella vecchia generazione dopo il gc minore è ancora maggiore dello spazio disponibile nella vecchia generazione, verrà attivato anche il gc completo. Dopo il gc completo, se presente non c'è ancora spazio per gli oggetti sopravvissuti dopo il minore gc, si verificherà anche "OOM".

Algoritmo di analisi della raggiungibilità

Analisi del meccanismo di caricamento delle classi JVM dal livello del codice sorgente JDK

Processo di caricamento della classe

Caricatori di classi e meccanismo di delega dei genitori

Perché progettare la delega dei genitori?

Spiegazione dettagliata del caricatore personalizzato Tomcat

Diversi caricatori di classi principali di Tomcat:

commonLoader: è possibile accedere alle classi nel percorso di caricamento dal contenitore Tomcat stesso e da ciascuna Webapp;

catalinaLoader: il caricatore di classi privato del contenitore Tomcat. Le classi nel percorso di caricamento non sono visibili alla Webapp;

sharedLoader: un caricatore di classi condiviso da ogni Webapp. Le classi nel percorso di caricamento sono visibili a tutte le Webapp, ma non al contenitore Tomcat;

WebappClassLoader: il caricatore di classi private di ogni Webapp è visibile solo alla Webapp corrente, ad esempio il caricamento delle classi correlate in un pacchetto war. Ogni applicazione del pacchetto war dispone del proprio WebappClassLoader per ottenere l'isolamento reciproco, ad esempio un pacchetto war diverso applicazioni. Vengono introdotte diverse versioni della molla in modo che l'implementazione possa caricare le rispettive versioni della molla;

modello di memoria

Algoritmo di raccolta dei rifiuti

netturbino

Implementazione dell'algoritmo sottostante per la garbage collection