Ottimizzazione delle prestazioni JVM per applicazioni di grandi dimensioni


I parametri JVM predefiniti non sono ottimali per l'esecuzione di applicazioni di grandi dimensioni. Qualsiasi intuizione da parte di persone che l'hanno sintonizzata su un'applicazione reale sarebbe utile. Stiamo eseguendo l'applicazione su una macchina Windows a 32 bit, in cui viene utilizzata la JVM client per impostazione predefinita. Abbiamo aggiunto-server e cambiato il NewRatio a 1: 3 (Una generazione giovane più grande).

Qualche altro parametro / tuning che hai provato e trovato utile?

[Aggiornamento] Il tipo specifico di applicazione sono parlando è un'applicazione server che sono raramente arresto, prendendo almeno-Xmx1024m. Si supponga inoltre che l'applicazione è già profilata. Sto cercando linee guida generali solo in termini di prestazioni JVM .

Author: amit, 2009-02-19

7 answers

Ci sono grandi quantità di queste informazioni in giro.

Innanzitutto, profila il codice prima di sintonizzare la JVM.

In secondo luogo, leggi attentamente la documentazione JVM ; ci sono un sacco di "leggende metropolitane" in giro. Ad esempio, il flag-server aiuta solo se la JVM rimane residente e in esecuzione per un po ' di tempo; - server "gira" il JIT/HotSpot, e che deve avere molti passaggi attraverso lo stesso percorso per essere alzato. - server, d'altra parte, rallenta esecuzione iniziale della JVM, poiché c'è più tempo di installazione.

Ci sono diversi buoni libri e siti web in giro. Vedi, ad esempio, http://www.javaperformancetuning.com /

 17
Author: Charlie Martin, 2013-10-09 07:21:08

Prefazione

Contesto

Stato in un negozio Java. Ha trascorso interi mesi dedicati all'esecuzione di test delle prestazioni su sistemi distribuiti, le principali app sono in Java. Alcuni dei quali implicano prodotti sviluppati e venduti da Sun stessi (poi Oracle).

Ripercorrerò le lezioni che ho imparato, un po 'di storia sulla JVM, alcuni discorsi sugli interni, un paio di parametri spiegati e infine un po' di messa a punto. Cercando di tenerlo al punto in modo da poterlo applicare in pratica.

Le cose stanno cambiando velocemente nel mondo Java, quindi parte di esso potrebbe essere già obsoleto dall'ultimo anno in cui ho fatto tutto questo. (Java 10 è già uscito?)

Buone pratiche

Cosa dovresti fare: Benchmark, BENCHMARK, BENCHMARK!

Quando hai davvero bisogno di conoscere le prestazioni, devi eseguire benchmark reali, specifici per il tuo carico di lavoro. Non ci sono alternative.

Inoltre, dovresti monitorare la JVM. Abilita il monitoraggio. Il le buone applicazioni di solito forniscono una pagina Web di monitoraggio e / o un'API. Altrimenti c'è lo strumento Java comune (JVisualVM, JMX, hprof e alcuni flag JVM).

Tieni presente che di solito non ci sono prestazioni da ottenere sintonizzando la JVM. È più un "per bloccarsi o non bloccarsi, trovando il punto di transizione". Si tratta di sapere che quando si assegna quella quantità di risorse alla propria applicazione, è possibile aspettarsi costantemente quella quantità di prestazioni in cambio. La conoscenza è potere.

Le prestazioni sono per lo più dettate dalla tua applicazione. Se vuoi più veloce, devi scrivere codice migliore.

Cosa farai la maggior parte del tempo: vivi con valori predefiniti sensibili affidabili

Non abbiamo tempo per ottimizzare e ottimizzare ogni singola applicazione là fuori. Il più delle volte vivremo semplicemente con impostazioni predefinite sensibili.

La prima cosa da fare quando si configura una nuova applicazione è leggere documentazione. La maggior parte delle applicazioni gravi viene fornito con una guida per la messa a punto delle prestazioni, compresi i consigli sulle impostazioni JVM.

Quindi è possibile configurare l'applicazione: JAVA_OPTS: -server -Xms???g -Xmx???g

  • -server: abilita le ottimizzazioni complete (questo flag è automatico sulla maggior parte delle JVM al giorno d'oggi)
  • -Xms -Xmx: imposta l'heap minimo e massimo (sempre lo stesso valore per entrambi, ovvero le uniche ottimizzazioni da fare).

Ben fatto, sai di tutta l'ottimizzazione parametri c'è da sapere sulla JVM, complimenti! Era semplice: D

Quello che NON farai MAI:

Per favore NON copiare la stringa casuale che hai trovato su Internet, specialmente quando prendono più righe come quella:

-server  -Xms1g -Xmx1g  -XX:PermSize=1g -XX:MaxPermSize=256m  -Xmn256m -Xss64k  -XX:SurvivorRatio=30  -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled  -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=10  -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark  -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Dsun.net.inetaddr.ttl=5  -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=`date`.hprof   -Dcom.sun.management.jmxremote.port=5616 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -server -Xms2g -Xmx2g -XX:MaxPermSize=256m -XX:NewRatio=1 -XX:+UseConcMarkSweepGC

Ad esempio, questa cosa trovata sulla prima pagina di Google è semplicemente terribile. Ci sono argomenti specificati multipli volte con valori in conflitto. Alcuni stanno solo forzando i valori predefiniti JVM (alla fine i valori predefiniti da 2 JVM versioni fa). Alcuni sono obsoleti e semplicemente ignorati. E finalmente almeno un parametro è così invalido che bloccherà costantemente la JVM all'avvio dalla sua mera esistenza.

Messa a punto effettiva

Come si sceglie la dimensione della memoria:

Leggi la guida dalla tua applicazione, dovrebbe dare qualche indicazione. Monitorare la produzione e regolare in seguito. Esegui alcuni benchmark se hai bisogno di precisione.

Nota importante: il processo java richiederà fino a heap massimo PIÙ 10% . L'overhead X % è la gestione dell'heap, non inclusa nell'heap stesso.

Tutta la memoria viene solitamente preallocata dal processo all'avvio. Potresti vedere il processo usando max heap TUTTO IL TEMPO. Semplicemente non è vero. È necessario utilizzare strumenti di monitoraggio Java per vedere cosa viene realmente utilizzato.

Trovare la giusta dimensione:

  • Se si blocca con OutOfMemoryException, non c'è abbastanza memoria
  • Se non si blocca con OutOfMemoryException, è troppa memoria
  • Se c'è troppa memoria MA l'hardware ce l'ha e/o è già pagato, è il perfetto numero, lavoro fatto!

JVM6 è bronzo, JVM7 è oro, JVM8 è platino...

La JVM sta migliorando per sempre. La garbage Collection è una cosa molto complessa e ci sono molte persone molto intelligenti che ci lavorano. Ha avuto enormi miglioramenti negli ultimi dieci anni e continuerà a farlo.

Anziché scopo informativo. Sono almeno 4 Garbage Collector disponibili in Oracle Java 7-8 (HotSpot) e OpenJDK 7-8. (Altre JVM possono essere completamente diverse, ad esempio Android, IBM, embedded):

  • SerialGC
  • ParallelGC
  • ConcurrentMarkSweepGC
  • G1GC
  • (più varianti e impostazioni)

[A partire da Java 7 in poi. Il codice Oracle e OpenJDK sono parzialmente condivisi. Il GC dovrebbe essere (principalmente) lo stesso su entrambi piattaforma.]

JVM > = 7 hanno molte ottimizzazioni e scelgono valori predefiniti decenti. Cambia un po ' per piattaforma. Bilancia più cose. Ad esempio, decidere di abilitare le ottimizzazioni multicore o meno se la CPU ha più core. Dovresti lasciarlo fare. Non modificare o forzare le impostazioni di GC.

Va bene lasciare che il computer prenda decisioni per te (è a questo che servono i computer). È meglio che le impostazioni JVM siano ottimali al 95% per tutto il tempo che forzare un "always 8 core aggressive collection for lower pause times" su tutte le scatole, metà delle quali t2.piccolo alla fine.

Eccezione: Quando l'applicazione viene fornita con una guida alle prestazioni e una messa a punto specifica. È perfettamente corretto lasciare le impostazioni fornite così come sono.

Suggerimento: passare a una JVM più recente per beneficiare degli ultimi miglioramenti a volte può fornire una buona spinta senza troppi sforzi.

Caso speciale: - XX: + UseCompressedOops

La JVM ha un'impostazione speciale che forza l'utilizzo interno dell'indice a 32 bit (leggi: come puntatori). Ciò consente di indirizzare 4 294 967 295 oggetti * 8 byte indirizzo = > 32 GB di memoria. (DA NON confondere con lo spazio degli indirizzi 4GB per puntatori REALI).

Riduce il consumo complessivo di memoria con un potenziale impatto positivo su tutti i livelli di caching.

Esempio di vita reale: la documentazione di ElasticSearch afferma che un 32bit 32GB in esecuzione il nodo può essere equivalente a un nodo da 40 GB a 64 BIT in termini di dati effettivi conservati in memoria.

Una nota sulla storia: Il flag era noto per essere instabile nell'era pre-java-7 (forse anche pre-java-6). Ha funzionato perfettamente nella nuova JVM per un po'.

Miglioramenti delle prestazioni della macchina virtuale Java HotSpot™

[...] In Java SE 7, l'uso di oops compressi è l'impostazione predefinita per i processi JVM a 64 bit quando-Xmx non è specificato e per i valori di-Xmx meno di 32 gigabyte. Per JDK 6 prima della versione 6u23, utilizzare il flag-XX:+UseCompressedOops con il comando java per abilitare la funzionalità.

Vedi : Ancora una volta la JVM è avanti di anni rispetto alla messa a punto manuale. Tuttavia, è interessante saperlo=)

Caso speciale: - XX: + UseNUMA

Non-Uniform Memory Access (NUMA) è un progetto di memoria del computer utilizzato in multiprocessing, il tempo di accesso alla memoria dipende dalla posizione di memoria relativa a processore. Fonte: Wikipedia

I sistemi moderni hanno architetture di memoria estremamente complesse con più livelli di memoria e cache, private e condivise, tra core e CPU.

Ovviamente l'accesso a un dato nella cache L2 nel processore corrente è MOLTO più veloce che dover andare fino in fondo a una memory stick da un altro socket.

Credo che tutti i sistemi multi - socket venduti oggi siano NUMA di progettazione, mentre tutti i sistemi dei consumatori NON lo sono. Controlla se il tuo server supporta NUMA con il comando numactl --show su linux.

Il flag NUMA-aware indica alla JVM di ottimizzare le allocazioni di memoria per la topologia hardware sottostante.

L'aumento delle prestazioni può essere sostanziale (cioè due cifre: +XX%). In effetti qualcuno che passa da un" NON-NUMA 10CPU 100GB "a un" NUMA 40CPU 400GB " potrebbe sperimentare una perdita [drammatica] nelle prestazioni se non conosce la bandiera.

Nota: Ci sono discussioni per rilevare NUMA e impostare automaticamente il flag nella JVM http://openjdk.java.net/jeps/163

Bonus: Tutte le applicazioni che intendono eseguire su hardware big fat (cioè NUMA) devono essere ottimizzate per questo. Non è specifico per le applicazioni Java.

Verso il futuro: - XX: + UseG1GC

L'ultimo miglioramento nella Garbage Collection è il G1 collector (leggi: Garbage First).

È destinato a nuclei alti, sistemi ad alta memoria. Al minimo assoluto 4 core + 6 GB di memoria. Si rivolge verso i database e le applicazioni ad alta intensità di memoria utilizzando 10 volte che e oltre.

Versione breve, a queste dimensioni i GC tradizionali si trovano ad affrontare troppi dati da elaborare contemporaneamente e le pause stanno sfuggendo di mano. Il G1 divide l'heap in molte piccole sezioni che possono essere gestite in modo indipendente e in parallelo mentre l'applicazione è in esecuzione.

La prima versione era disponibile nel 2013. Lo è abbastanza maturo per la produzione ora, ma non andrà come predefinito in qualunque momento presto. Vale la pena provare per le applicazioni di grandi dimensioni.

Non toccare: Dimensioni di generazione (NewGen, PermGen...)

Il GC divide la memoria in più sezioni. (Non entrando nei dettagli, puoi google "Java GC Generations".)

L'ultima volta che ho trascorso una settimana per provare 20 diverse combinazioni di bandiere di generazioni su un'app che prendeva 10000 hit / s. Stavo ricevendo una magnifica spinta che andava da -1% a + 1%.

Le generazioni Java GC sono un argomento interessante su cui leggere o scrivere articoli. Non sono una cosa da sintonizzare a meno che tu non faccia parte dell '1% che può dedicare molto tempo a guadagni trascurabili tra l' 1% di persone che hanno davvero bisogno di ottimizzazioni.

Conclusione

Spero che questo possa aiutarti. Buon divertimento con la JVM.

Java è il miglior linguaggio e la migliore piattaforma al mondo! Vai a diffondere l'amore: D

 19
Author: user5994461, 2016-05-25 22:20:10

Guarda qui (o fai una ricerca su Google per l'ottimizzazione dell'hotspot) http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

Sicuramente vuoi profilare la tua app prima di provare a sintonizzare la vm. NetBeans ha un bel profiler integrato che ti permetterà di vedere ogni sorta di cose.

Una volta qualcuno mi ha detto che il GC era rotto per la loro app-ho guardato il codice e ho scoperto che non hanno mai chiuso nessuno dei risultati delle query del database in modo che stessero mantenendo enormi quantità di array di byte. Una volta chiusi i risultati, il tempo è passato da oltre 20 minuti e un GB di memoria a circa 2 minuti e una quantità molto piccola di memoria. Sono stati in grado di rimuovere i parametri di sintonizzazione JVM e le cose erano felici.

 7
Author: TofuBeer, 2009-02-19 05:54:27

Ti suggerisco di profilare la tua applicazione con il campionamento della CPU e il monitoraggio dell'allocazione degli oggetti attivati allo stesso tempo. Troverai risultati molto diversi che possono essere utili per sintonizzare il tuo codice. Prova anche a utilizzare il profiler hprof integrato, può anche dare risultati molto diversi.

In generale la profilazione della tua applicazione fa molta più differenza rispetto agli args JVM.

 1
Author: Peter Lawrey, 2009-02-19 22:48:04

Il modo migliore per rispondere a questo è eseguire test controllati sull'applicazione nel modo più vicino a un ambiente di "produzione" possibile creare. È del tutto possibile che l'uso di-server, una dimensione heap iniziale ragionevole e il comportamento relativamente intelligente delle JVM recenti si comportino bene o meglio della stragrande maggioranza delle impostazioni che normalmente si proverebbero.

Esiste un'eccezione specifica a questa ampia generalizzazione: nel caso in cui si stia eseguendo in un contenitore Web, c'è una probabilità davvero alta che si desidera aumentare le impostazioni di generazione permanente.

 1
Author: , 2009-02-21 10:22:43

Java sulla macchina Windows a 32 bit, le tue scelte sono limitate. Nella mia esperienza, l'impostazione dei parametri follow influirà sulle prestazioni dell'applicazione:

  1. dimensioni della memoria
  2. scelta dei collettori GC
  3. parametri relativi ai collettori GC
 1
Author: stones333, 2012-10-15 23:07:14

Ciò dipenderà in larga misura dall'applicazione e dal fornitore e dalla versione della JVM. Devi essere chiaro su ciò che consideri un problema di prestazioni. Sei interessato a determinate sezioni critiche del codice? Avete profilato l'applicazione ancora? La JVM sta trascorrendo troppo tempo a raccogliere rifiuti?

Probabilmente inizierei con l'opzione-verbose:gc JVM per vedere come funziona la raccolta dei rifiuti. Molte volte, la soluzione più semplice per aumentare la dimensione massima dell'heap con - Xmx . Se impari a interpretare l'output-verbose: gc, ti dirà quasi tutto ciò che devi sapere sulla messa a punto della JVM nel suo complesso. Ma fare questo da solo non renderà magicamente il codice mal sintonizzato semplicemente più veloce. La maggior parte delle opzioni di ottimizzazione JVM sono progettate per migliorare le prestazioni del garbage collector e/o le dimensioni della memoria.

Per la profilazione, mi piace yourkit.com

 0
Author: Gary, 2009-02-19 05:38:31