Introduzione a JVM Code Cache

1. Introduzione

In questo tutorial, daremo una rapida occhiata e impareremo a conoscere la memoria cache del codice di JVM.

2. Che cos'è la cache del codice?

In poche parole, JVM Code Cache è un'area in cui JVM memorizza il proprio bytecode compilato in codice nativo . Chiamiamo ogni blocco del codice nativo eseguibile un nmethod . Il metodo n potrebbe essere un metodo Java completo o inline.

Il compilatore just-in-time (JIT) è il più grande consumatore dell'area della cache del codice. Ecco perché alcuni sviluppatori chiamano questa memoria una cache di codice JIT.

3. Regolazione della cache del codice

La cache del codice ha una dimensione fissa . Una volta che è pieno, la JVM non compilerà alcun codice aggiuntivo poiché il compilatore JIT è ora disattivato. Inoltre, riceveremo il messaggio di avviso "CodeCache è pieno ... Il compilatore è stato disabilitato ". Di conseguenza, ci ritroveremo con prestazioni ridotte nella nostra applicazione. Per evitare ciò, possiamo ottimizzare la cache del codice con le seguenti opzioni di dimensione:

  • InitialCodeCacheSize : la dimensione della cache del codice iniziale, 160 KB predefinito
  • ReservedCodeCacheSize : la dimensione massima predefinita è 48 MB
  • CodeCacheExpansionSize : la dimensione di espansione della cache del codice, 32 KB o 64 KB

L'aumento di ReservedCodeCacheSize può essere una soluzione, ma in genere si tratta solo di una soluzione temporanea.

Fortunatamente, la JVM offre un'opzione UseCodeCacheFlushing per controllare lo svuotamento dell'area della cache del codice . Il suo valore predefinito è false. Quando lo abilitiamo, libera l'area occupata quando sono soddisfatte le seguenti condizioni:

  • la cache del codice è piena; quest'area viene svuotata se la sua dimensione supera una certa soglia
  • l'intervallo determinato è trascorso dall'ultima pulizia
  • il codice precompilato non è abbastanza caldo. Per ogni metodo compilato la JVM tiene traccia di uno speciale contatore di piccantezza. Se il valore di questo contatore è inferiore a una soglia calcolata, la JVM libera questa parte di codice precompilato

4. Utilizzo della cache del codice

Per monitorare l'utilizzo della cache del codice, è necessario tenere traccia della dimensione della memoria attualmente in uso.

Per ottenere informazioni sull'utilizzo della cache del codice, è possibile specificare l' opzione –XX: + PrintCodeCache JVM . Dopo aver eseguito la nostra applicazione, vedremo un output simile:

CodeCache: size=32768Kb used=542Kb max_used=542Kb free=32226Kb 

Vediamo cosa significano ciascuno di questi valori:

  • size nell'output mostra la dimensione massima della memoria, che è identica a ReservedCodeCacheSize
  • utilizzata è la dimensione effettiva della memoria attualmente in uso
  • max_used è la dimensione massima che è stata utilizzata
  • libera è la memoria rimanente che non è ancora occupata

L' opzione PrintCodeCache è molto utile, in quanto possiamo:

  • vedere quando avviene il lavaggio
  • determinare se abbiamo raggiunto un punto critico di utilizzo della memoria

5. Cache di codice segmentata

A partire da Java 9, la JVM divide la cache del codice in tre segmenti distinti, ciascuno dei quali contiene un particolare tipo di codice compilato . Per essere più precisi, ci sono tre segmenti:

  • Il segmento non di metodo contiene codice correlato interno JVM come l'interprete bytecode. Per impostazione predefinita, questo segmento è di circa 5 MB. Inoltre, è possibile configurare la dimensione del segmento tramite il flag di ottimizzazione -XX: NonNMethodCodeHeapSize
  • Il segmento di codice profilato contiene codice leggermente ottimizzato con tempi di vita potenzialmente brevi. Anche se la dimensione del segmento è di circa 122 MB per impostazione predefinita, possiamo cambiarla tramite il flag di ottimizzazione -XX: ProfiledCodeHeapSize
  • Il segmento non profilato contiene codice completamente ottimizzato con durate potenzialmente lunghe. Allo stesso modo, è di circa 122 MB per impostazione predefinita. Questo valore è, ovviamente, configurabile tramite il flag di ottimizzazione -XX: NonProfiledCodeHeapSize

Questa nuova struttura tratta i vari tipi di codice conforme in modo diverso, il che porta a migliori prestazioni complessive.

Ad esempio, separare il codice compilato di breve durata dal codice di lunga durata migliora le prestazioni dello sweeper del metodo, principalmente perché è necessario eseguire la scansione di una regione di memoria più piccola.

6. Conclusione

Questo rapido articolo presenta una breve introduzione alla cache del codice JVM.

Inoltre, abbiamo presentato alcune opzioni di utilizzo e ottimizzazione per monitorare e diagnosticare questa area di memoria.