Esplorazione dei flag di ottimizzazione di JVM

1. Panoramica

È possibile sintonizzare HotSpot JVM con una varietà di flag di ottimizzazione. Poiché esistono centinaia di tali flag, tenerne traccia e tenere traccia dei loro valori predefiniti può essere un po 'scoraggiante.

In questo tutorial, introdurremo alcuni modi per scoprire tali flag di ottimizzazione e impareremo come lavorarci.

2. Panoramica delle opzioni Java

Il comando java supporta un'ampia varietà di flag che rientrano nelle seguenti categorie:

  • Opzioni standard che sono garantite per essere supportate da tutte le implementazioni JVM disponibili. Di solito, queste opzioni vengono utilizzate per azioni quotidiane come –classpath, -cp, –version e così via
  • Opzioni aggiuntive che non sono supportate da tutte le implementazioni JVM e sono generalmente soggette a modifiche. Queste opzioni iniziano con -X

Tieni presente che non dovremmo usare queste opzioni extra casualmente. Inoltre, alcune di queste opzioni aggiuntive sono più avanzate e iniziano con -XX .

In questo articolo, ci concentreremo sui flag -XX più avanzati .

3. Flag di ottimizzazione JVM

Per elencare i flag di ottimizzazione JVM globali, possiamo abilitare il flag PrintFlagsFinal come segue:

>> java -XX:+PrintFlagsFinal -version [Global flags] uintx CodeCacheExpansionSize = 65536 {pd product} {default} bool CompactStrings = true {pd product} {default} bool DoEscapeAnalysis = true {C2 product} {default} double G1ConcMarkStepDurationMillis = 10.000000 {product} {default} size_t G1HeapRegionSize = 1048576 {product} {ergonomic} uintx MaxHeapFreeRatio = 70 {manageable} {default} // truncated openjdk version "14" 2020-03-17 OpenJDK Runtime Environment (build 14+36-1461) OpenJDK 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)

Come mostrato sopra, alcuni flag hanno valori predefiniti per questa particolare versione di JVM.

I valori predefiniti per alcuni flag possono essere diversi su piattaforme diverse, come mostrato nella colonna finale. Ad esempio, il prodotto significa che l'impostazione predefinita del flag è uniforme su tutte le piattaforme; il prodotto pd significa che l'impostazione predefinita del flag dipende dalla piattaforma. I valori gestibili possono essere modificati dinamicamente in fase di esecuzione.

3.1. Flag diagnostici

Il flag PrintFlagsFinal , tuttavia, non mostra tutti i flag di ottimizzazione possibili. Ad esempio, per vedere anche i flag di ottimizzazione diagnostica, dovremmo aggiungere il flag UnlockDiagnosticVMOptions :

>> java -XX:+PrintFlagsFinal -version | wc -l 557 >> java -XX:+PrintFlagsFinal -XX:+UnlockDiagnosticVMOptions -version | wc -l 728

Chiaramente, ci sono un paio di centinaia di flag in più quando includiamo le opzioni diagnostiche. Ad esempio, la stampa delle statistiche di tracciamento della memoria nativa è disponibile solo come parte dei flag di diagnostica:

bool PrintNMTStatistics = false {diagnostic} {default}

3.2. Bandiere sperimentali

Per vedere anche le opzioni sperimentali, dovremmo aggiungere il flag UnlockExperimentalVMOptions :

>> java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version | wc -l 809

3.3. Bandiere JVMCI

A partire da Java 9, l'interfaccia del compilatore JVM o JVMCI ci consente di utilizzare un compilatore scritto in Java, come Graal, come compilatore dinamico.

Per vedere le opzioni relative a JVMCI, dovremmo aggiungere qualche altro flag e anche abilitare JVMCI:

>> java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions \ >> -XX:+JVMCIPrintProperties -XX:+EnableJVMCI -XX:+PrintFlagsFinal -version | wc -l 1516

La maggior parte delle volte, tuttavia, l'utilizzo di opzioni globali, diagnostiche e sperimentali dovrebbe essere sufficiente e ci aiuterà a trovare la bandiera che abbiamo in mente.

3.4. Mettere tutto insieme

Queste combinazioni di opzioni possono aiutarci a trovare un flag di regolazione, soprattutto quando non ricordiamo il nome esatto. Ad esempio, per trovare il flag di ottimizzazione relativo ai riferimenti flessibili in Java:

>> alias jflags="java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version" >> jflags | grep Soft size_t SoftMaxHeapSize = 4294967296 {manageable} {ergonomic} intx SoftRefLRUPolicyMSPerMB = 1000 {product} {default}

Dal risultato, possiamo facilmente intuire che SoftRefLRUPolicyMSPerMB è la bandiera che stiamo cercando.

4. Diversi tipi di bandiere

Nella sezione precedente, abbiamo sorvolato su un argomento importante: i tipi di flag. Diamo un'altra occhiata all'output java -XX: + PrintFlagsFinal -version :

[Global flags] uintx CodeCacheExpansionSize = 65536 {pd product} {default} bool CompactStrings = true {pd product} {default} bool DoEscapeAnalysis = true {C2 product} {default} double G1ConcMarkStepDurationMillis = 10.000000 {product} {default} size_t G1HeapRegionSize = 1048576 {product} {ergonomic} uintx MaxHeapFreeRatio = 70 {manageable} {default} // truncated

Come mostrato sopra, ogni bandiera ha un tipo specifico.

Le opzioni booleane vengono utilizzate per abilitare o disabilitare una funzione . Tali opzioni non richiedono un valore. Per abilitarli, dobbiamo solo mettere un segno più prima del nome dell'opzione:

-XX:+PrintFlagsFinal

Al contrario, per disabilitarli, dobbiamo aggiungere un segno meno prima del loro nome:

-XX:-RestrictContended

Altri tipi di flag richiedono un valore di argomento. È possibile separare il valore dal nome dell'opzione con uno spazio, due punti, un segno di uguale o l'argomento può seguire direttamente il nome dell'opzione (la sintassi esatta è diversa per ciascuna opzione):

-XX:ObjectAlignmentInBytes=16 -Xms5g -Xlog:gc

5. Documentazione e codice sorgente

Trovare il nome giusto per la bandiera è una cosa. Trovare cosa sta facendo quella particolare bandiera sotto il cofano è un'altra storia.

Un modo per scoprire questo tipo di dettagli è guardare la documentazione. Ad esempio, la documentazione per il comando java nella sezione delle specifiche degli strumenti JDK è un ottimo punto di partenza.

A volte, nessuna quantità di documentazione può battere il codice sorgente. Pertanto, se abbiamo il nome di una particolare bandiera, allora possiamo esplorare il codice sorgente JVM per scoprire cosa sta succedendo.

Ad esempio, possiamo controllare il codice sorgente di HotSpot JVM da GitHub o anche dal loro repository Mercurial e poi:

>> git clone [email protected]:openjdk/jdk14u.git openjdk >> cd openjdk/src/hotspot >> grep -FR 'PrintFlagsFinal' . ./share/runtime/globals.hpp: product(bool, PrintFlagsFinal, false, ./share/runtime/init.cpp: if (PrintFlagsFinal || PrintFlagsRanges) {

Qui stiamo cercando tutti i file contenenti la stringa PrintFlagsFinal . Dopo aver trovato i file responsabili, possiamo guardarci intorno e vedere come funziona quel flag specifico.

6. Conclusione

In questo articolo, abbiamo visto come trovare quasi tutti i flag di ottimizzazione JVM disponibili e abbiamo anche imparato alcuni trucchi per lavorare con loro in modo più efficace.