Applicazione lenta, frequenti blocchi JVM con configurazioni a CPU singola e Java 12+


24

Abbiamo un'applicazione client (con oltre 10 anni di sviluppo). Il suo JDK è stato recentemente aggiornato da OpenJDK 11 a OpenJDK 14. Sulle configurazioni di Windows 10 a CPU singola (hyper-threading disabilitato) (e all'interno di macchine VirtualBox con una sola CPU disponibile) l'applicazione si avvia piuttosto lentamente rispetto a Java 11. Inoltre, utilizza la CPU al 100% per la maggior parte del tempo. Potremmo anche riprodurre il problema con l'impostazione dell'affinità del processore su una sola CPU ( c:\windows\system32\cmd.exe /C start /affinity 1 ...).

Alcune misurazioni con l'avvio dell'applicazione e l'esecuzione di una query con interazione manuale minima nella mia macchina VirtualBox:

  • OpenJDK 11.0.2: 36 secondi
  • OpenJDK 13.0.2: ~ 1,5 minuti
  • OpenJDK 13.0.2 con -XX:-UseBiasedLocking: 46 secondi
  • OpenJDK 13.0.2 con -XX:-ThreadLocalHandshakes: 40 secondi
  • OpenJDK 14: 5-6 minuti
  • OpenJDK 14 con -XX:-UseBiasedLocking: 3-3,5 minuti
  • OpenJDK 15 EA Build 20: ~ 4,5 minuti

È stato modificato solo il JDK usato (e le opzioni menzionate). ( -XX:-ThreadLocalHandshakesnon disponibile in Java 14.)

Abbiamo provato a registrare ciò che fa JDK 14 -Xlog:all=debug:file=app.txt:uptime,tid,level,tags:filecount=50.

Il conteggio delle linee di registro per ogni secondo sembra abbastanza fluido con OpenJDK 11.0.2:

$ cat jdk11-log/app* | grep "^\[" | cut -d. -f 1 | cut -d[ -f 2 | sort | uniq -c | sort -k 2 -n
  30710 0
  44012 1
  55461 2
  55974 3
  27182 4
  41292 5
  43796 6
  51889 7
  54170 8
  58850 9
  51422 10
  44378 11
  41405 12
  53589 13
  41696 14
  29526 15
   2350 16
  50228 17
  62623 18
  42684 19
  45045 20

D'altra parte, OpenJDK 14 sembra avere periodi di quiete interessanti:

$ cat jdk14-log/app* | grep "^\[" | cut -d. -f 1 | cut -d[ -f 2 | sort | uniq -c | sort -k 2 -n
   7726 0
   1715 5
  10744 6
   4341 11
  42792 12
  45979 13
  38783 14
  17253 21
  34747 22
   1025 28
   2079 33
   2398 39
   3016 44

Quindi, cosa sta succedendo tra i secondi 1-4, 7-10 e 14-20?

...
[0.350s][7248][debug][class,resolve        ] jdk.internal.ref.CleanerFactory$1 java.lang.Thread CleanerFactory.java:45
[0.350s][7248][debug][class,resolve        ] jdk.internal.ref.CleanerImpl java.lang.Thread CleanerImpl.java:117
[0.350s][7248][info ][biasedlocking        ] Aligned thread 0x000000001727e010 to 0x000000001727e800
[0.350s][7248][info ][os,thread            ] Thread started (tid: 2944, attributes: stacksize: default, flags: CREATE_SUSPENDED STACK_SIZE_PARAM_IS)
[0.350s][6884][info ][os,thread            ] Thread is alive (tid: 6884).
[0.350s][6884][debug][os,thread            ] Thread 6884 stack dimensions: 0x00000000175b0000-0x00000000176b0000 (1024k).
[0.350s][6884][debug][os,thread            ] Thread 6884 stack guard pages activated: 0x00000000175b0000-0x00000000175b4000.
[0.350s][7248][debug][thread,smr           ] tid=7248: Threads::add: new ThreadsList=0x0000000017254500
[0.350s][7248][debug][thread,smr           ] tid=7248: ThreadsSMRSupport::free_list: threads=0x0000000017253d50 is freed.
[0.350s][2944][info ][os,thread            ] Thread is alive (tid: 2944).
[0.350s][2944][debug][os,thread            ] Thread 2944 stack dimensions: 0x00000000177b0000-0x00000000178b0000 (1024k).
[0.350s][2944][debug][os,thread            ] Thread 2944 stack guard pages activated: 0x00000000177b0000-0x00000000177b4000.
[0.351s][2944][debug][class,resolve        ] java.lang.Thread java.lang.Runnable Thread.java:832
[0.351s][2944][debug][class,resolve        ] jdk.internal.ref.CleanerImpl jdk.internal.misc.InnocuousThread CleanerImpl.java:135
[0.351s][2944][debug][class,resolve        ] jdk.internal.ref.CleanerImpl jdk.internal.ref.PhantomCleanable CleanerImpl.java:138
[0.351s][2944][info ][biasedlocking,handshake] JavaThread 0x000000001727e800 handshaking JavaThread 0x000000000286d800 to revoke object 0x00000000c0087f78
[0.351s][2944][debug][vmthread               ] Adding VM operation: HandshakeOneThread
[0.351s][6708][debug][vmthread               ] Evaluating non-safepoint VM operation: HandshakeOneThread
[0.351s][6708][debug][vmoperation            ] begin VM_Operation (0x00000000178af250): HandshakeOneThread, mode: no safepoint, requested by thread 0x000000001727e800

# no log until 5.723s

[5.723s][7248][info ][biasedlocking          ]   Revoked bias of currently-unlocked object
[5.723s][7248][debug][handshake,task         ] Operation: RevokeOneBias for thread 0x000000000286d800, is_vm_thread: false, completed in 94800 ns
[5.723s][7248][debug][class,resolve          ] java.util.zip.ZipFile$CleanableResource java.lang.ref.Cleaner ZipFile.java:715
[5.723s][7248][debug][class,resolve          ] java.lang.ref.Cleaner jdk.internal.ref.CleanerImpl$PhantomCleanableRef Cleaner.java:220
[5.723s][7248][debug][class,resolve          ] java.util.zip.ZipFile$CleanableResource java.util.WeakHashMap ZipFile.java:716
...

La seconda pausa un po 'più tardi:

...
[6.246s][7248][info ][class,load              ] java.awt.Graphics source: jrt:/java.desktop
[6.246s][7248][debug][class,load              ]  klass: 0x0000000100081a00 super: 0x0000000100001080 loader: [loader data: 0x0000000002882bd0 of 'bootstrap'] bytes: 5625 checksum: 0025818f
[6.246s][7248][debug][class,resolve           ] java.awt.Graphics java.lang.Object (super)
[6.246s][7248][info ][class,loader,constraints] updating constraint for name java/awt/Graphics, loader 'bootstrap', by setting class object
[6.246s][7248][debug][jit,compilation         ]   19       4       java.lang.Object::<init> (1 bytes)   made not entrant
[6.246s][7248][debug][vmthread                ] Adding VM operation: HandshakeAllThreads
[6.246s][6708][debug][vmthread                ] Evaluating non-safepoint VM operation: HandshakeAllThreads
[6.246s][6708][debug][vmoperation             ] begin VM_Operation (0x000000000203ddf8): HandshakeAllThreads, mode: no safepoint, requested by thread 0x000000000286d800
[6.246s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026b0800, is_vm_thread: true, completed in 1400 ns
[6.246s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026bb800, is_vm_thread: true, completed in 700 ns
[6.246s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026ef800, is_vm_thread: true, completed in 100 ns
[6.246s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026f0800, is_vm_thread: true, completed in 100 ns
[6.246s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026f1800, is_vm_thread: true, completed in 100 ns
[6.246s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026f4800, is_vm_thread: true, completed in 100 ns
[6.247s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x0000000002768800, is_vm_thread: true, completed in 100 ns
[6.247s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x000000000276e000, is_vm_thread: true, completed in 100 ns
[6.247s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x0000000017268800, is_vm_thread: true, completed in 100 ns
[6.247s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x000000001727e800, is_vm_thread: true, completed in 800 ns

# no log until 11.783s

[11.783s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x000000000286d800, is_vm_thread: true, completed in 6300 ns
[11.783s][6708][info ][handshake               ] Handshake "Deoptimize", Targeted threads: 11, Executed by targeted threads: 0, Total completion time: 5536442500 ns
[11.783s][6708][debug][vmoperation             ] end VM_Operation (0x000000000203ddf8): HandshakeAllThreads, mode: no safepoint, requested by thread 0x000000000286d800
[11.783s][7248][debug][protectiondomain        ] Checking package access
[11.783s][7248][debug][protectiondomain        ] class loader: a 'jdk/internal/loader/ClassLoaders$AppClassLoader'{0x00000000c0058628} protection domain: a 'java/security/ProtectionDomain'{0x00000000c058b948} loading: 'java/awt/Graphics'
[11.783s][7248][debug][protectiondomain        ] granted
[11.783s][7248][debug][class,resolve           ] sun.launcher.LauncherHelper java.awt.Graphics LauncherHelper.java:816 (reflection)
[11.783s][7248][debug][class,resolve           ] jdk.internal.reflect.Reflection [Ljava.lang.reflect.Method; Reflection.java:300
[11.783s][7248][debug][class,preorder          ] java.lang.PublicMethods$MethodList source: C:\Users\example\AppData\Local\example\stable\jdk\lib\modules
...

Quindi il terzo:

...
[14.578s][7248][debug][class,preorder          ] java.lang.InheritableThreadLocal source: C:\Users\example\AppData\Local\example\stable\jdk\lib\modules
[14.578s][7248][info ][class,load              ] java.lang.InheritableThreadLocal source: jrt:/java.base
[14.578s][7248][debug][class,load              ]  klass: 0x0000000100124740 super: 0x0000000100021a18 loader: [loader data: 0x0000000002882bd0 of 'bootstrap'] bytes: 1338 checksum: 8013ed55
[14.578s][7248][debug][class,resolve           ] java.lang.InheritableThreadLocal java.lang.ThreadLocal (super)
[14.578s][7248][debug][jit,compilation         ]  699       3       java.lang.ThreadLocal::get (38 bytes)   made not entrant
[14.578s][7248][debug][vmthread                ] Adding VM operation: HandshakeAllThreads
[14.578s][6708][debug][vmthread                ] Evaluating non-safepoint VM operation: HandshakeAllThreads
[14.578s][6708][debug][vmoperation             ] begin VM_Operation (0x000000000203d228): HandshakeAllThreads, mode: no safepoint, requested by thread 0x000000000286d800
[14.578s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026b0800, is_vm_thread: true, completed in 1600 ns
[14.578s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026bb800, is_vm_thread: true, completed in 900 ns
[14.578s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026ef800, is_vm_thread: true, completed in 100 ns
[14.578s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026f0800, is_vm_thread: true, completed in 100 ns
[14.578s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026f1800, is_vm_thread: true, completed in 100 ns
[14.578s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x00000000026f4800, is_vm_thread: true, completed in 0 ns
[14.578s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x0000000002768800, is_vm_thread: true, completed in 0 ns
[14.578s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x000000000276e000, is_vm_thread: true, completed in 0 ns
[14.578s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x0000000017268800, is_vm_thread: true, completed in 0 ns
[14.579s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x000000001727e800, is_vm_thread: true, completed in 900 ns

# no log until 21.455s

[21.455s][6708][debug][handshake,task          ] Operation: Deoptimize for thread 0x000000000286d800, is_vm_thread: true, completed in 12100 ns
[21.455s][6708][info ][handshake               ] Handshake "Deoptimize", Targeted threads: 11, Executed by targeted threads: 0, Total completion time: 6876829000 ns
[21.455s][6708][debug][vmoperation             ] end VM_Operation (0x000000000203d228): HandshakeAllThreads, mode: no safepoint, requested by thread 0x000000000286d800
[21.455s][7248][debug][class,resolve           ] sun.security.jca.Providers java.lang.InheritableThreadLocal Providers.java:39
[21.455s][7248][info ][class,init              ] 1251 Initializing 'java/lang/InheritableThreadLocal'(no method) (0x0000000100124740)
[21.455s][7248][debug][class,resolve           ] java.lang.InheritableThreadLocal java.lang.ThreadLocal InheritableThreadLocal.java:57
[21.456s][7248][debug][class,preorder          ] sun.security.jca.ProviderList source: C:\Users\example\AppData\Local\example\stable\jdk\lib\modules
[21.456s][7248][info ][class,load              ] sun.security.jca.ProviderList source: jrt:/java.base
[21.456s][7248][debug][class,load              ]  klass: 0x00000001001249a8 super: 0x0000000100001080 loader: [loader data: 0x0000000002882bd0 of 'bootstrap'] bytes: 11522 checksum: bdc239d2
[21.456s][7248][debug][class,resolve           ] sun.security.jca.ProviderList java.lang.Object (super)
...

Le seguenti due righe sembrano interessanti:

[11.783s][6708][info ][handshake               ] Handshake "Deoptimize", Targeted threads: 11, Executed by targeted threads: 0, Total completion time: 5536442500 ns
[21.455s][6708][info ][handshake               ] Handshake "Deoptimize", Targeted threads: 11, Executed by targeted threads: 0, Total completion time: 6876829000 ns

È normale che queste strette di mano abbiano impiegato 5,5 e 6,8 secondi?

Ho riscontrato lo stesso rallentamento (e registri simili) con l' app demo update4j (che non è completamente correlata alla nostra applicazione) in esecuzione con questo comando:

Z:\swing>\jdk-14\bin\java -Xlog:all=debug:file=app.txt:uptime,tid,level,tags:filecount=50 \
    -jar update4j-1.4.5.jar --remote http://docs.update4j.org/demo/setup.xml

Cosa devo cercare per rendere di nuovo più veloce la nostra app su configurazioni Windows 10 a CPU singola? Posso risolvere questo problema modificando qualcosa nella nostra applicazione o aggiungendo argomenti JVM?

È un bug JDK, devo segnalarlo?

aggiornamento 2020-04-25:

Per quanto vedo i file di registro contiene anche registri GC. Questi sono i primi registri GC:

$ cat app.txt.00 | grep "\[gc"
[0.016s][7248][debug][gc,heap          ] Minimum heap 8388608  Initial heap 60817408  Maximum heap 1073741824
[0.017s][7248][info ][gc,heap,coops    ] Heap address: 0x00000000c0000000, size: 1024 MB, Compressed Oops mode: 32-bit
[0.018s][7248][info ][gc               ] Using Serial
[22.863s][6708][info ][gc,start                ] GC(0) Pause Young (Allocation Failure)
[22.863s][6708][debug][gc,heap                 ] GC(0) Heap before GC invocations=0 (full 0): def new generation   total 17856K, used 15936K [0x00000000c0000000, 0x00000000c1350000, 0x00000000d5550000)
...

Sfortunatamente non sembra correlato poiché inizia dopo la terza pausa.

aggiornamento 2020-04-26:

Con OpenJDK 14 l'applicazione utilizza il 100% di CPU nella mia macchina VirtualBox (a CPU singola) (in esecuzione su una CPU i7-6600U). La macchina virtuale ha 3,5 GB di RAM. Secondo Task Manager il 40% + è gratuito e l'attività del disco è 0% (suppongo che ciò significhi non scambiare). L'aggiunta di un'altra CPU alla macchina virtuale (e l'abilitazione dell'hyper-threading per le macchine fisiche) rendono di nuovo l'applicazione abbastanza veloce. Mi chiedo solo, è stato un compromesso intenzionale durante lo sviluppo di JDK la perdita di prestazioni su macchine (rare) a CPU singola per rendere la JVM più veloce su CPU multicore / hyper-threading?


3
Non -Xlog:all=debugattivare la registrazione di GC? Sarebbe la mia prima ipotesi per eventuali pause.
kichik,

Hai provato a correre con un profiler e confrontare i risultati? Penso che sarebbe la cosa naturale da fare.
Axel

1
controlla anche i messaggi di sistema di Windows, prova una build diversa per jdk 14. Se tutto il resto fallisce, aumenta il problema?
Khanna111,

1
@ Yan.F: OpenJDK 11 non sarà supportato per sempre, è tempo di preparare le nuove versioni e i bug. Inoltre, sembra un bug JDK - che potrebbe essere corretto o meno ma potrebbe aiutare anche gli altri. Ad ogni modo, per me è soprattutto curiosità. D'altra parte ora vorrei sapere cosa dire ai nostri clienti come requisiti minimi di sistema per la nostra app.
palacsint,

1
@ Khanna111: Sì, l'ho appena scritto come risposta.
palacsint

Risposte:


6

In base alla mia esperienza, i problemi di prestazioni con JDK sono legati principalmente a uno dei seguenti:

  • Compilazione di JIT
  • Configurazione VM (dimensioni heap)
  • Algoritmo GC
  • Cambiamenti nella JVM / JDK che interrompono le note applicazioni in esecuzione
  • (Oh, e ho dimenticato di menzionare il caricamento della lezione ...)

Se usi semplicemente la configurazione JVM predefinita da OpenJDK11, forse dovresti impostare alcune delle opzioni più importanti su valori fissi, come GC, Dimensione heap, ecc.

Forse alcuni strumenti di analisi grafica potrebbero aiutarti a rintracciare il tuo problema. Come Retrace, AppDynamics o FlightRecorder e simili. Questi offrono una panoramica più completa dello stato generale di heap, cicli gc, RAM, thread, carico della CPU e così via in un dato momento di quanto i file di registro possano fornire.

Comprendo correttamente che l'applicazione scrive circa 30710 righe nel registro entro il primo secondo di esecuzione (in OpenJDK11)? Perché è "solo" scrivere circa 7k righe in OpenJDK14 nel primo secondo? Questa sembra un'enorme differenza per un'applicazione che è appena stata avviata su JVM diverse per me ... Sei sicuro che non ci siano ad esempio quantità elevate di stack stack di eccezioni scaricati nel registro?
Gli altri numeri sono persino più alti a volte, quindi forse i rallentamenti sono legati alla registrazione delle eccezioni? O addirittura lo scambio, se la RAM si sta esaurendo?
In realtà sto pensando, se un'applicazione non scrive nulla nel registro, questo è un segno di regolare funzionamento senza problemi (a meno che non sia completamente congelato in questo momento). Quello che sta succedendo dai secondi 12-22 (nel caso OpenJDK14 qui) è ciò che mi preoccuperebbe di più ... le linee registrate passano attraverso il tetto ... perché ?
E dopo la registrazione scende a tutti i valori bassi di tempo di circa 1-2K linee ... qual è la ragione per cui ?? (beh, forse è il GC a dare il via al secondo 22 e fa una tabula rasa che risolve alcune cose ...?)

Un'altra cosa potrebbe essere la tua affermazione sulle macchine a "singola CPU". Ciò implica anche "single core" (Idk, forse il tuo software è personalizzato su hardware legacy o qualcosa del genere)? E le VM "single CPU" sono in esecuzione su quelle macchine? Ma presumo, mi sbaglio su questi presupposti, dal momento che quasi tutte le CPU sono multicore al giorno d'oggi ... ma avrei indagato su un problema di multithreading (deadlock) forse.


2
Per favore, non usare firme o slogan nei tuoi post, il ripetitivo "GL e HF" è considerato rumore e una distrazione dal contenuto del tuo post qui. Vedi meta.stackexchange.com/help/behavior per maggiori informazioni.
meagar

1
"Comprendo correttamente che l'applicazione scrive circa 30710 righe nel registro entro il primo secondo di esecuzione (in OpenJDK11)?" - Sì hai ragione.
palacsint,

1
"Sei sicuro che non ci siano ad esempio quantità elevate di stacktracce di eccezioni scaricate nel registro?" - Il registro è pulito, non ho trovato nulla di strano lì, l'applicazione funziona correttamente (tranne che funziona molto lentamente).
palacsint,

1
GC sta dando il via al 22 ° secondo e l'app rimane lenta dopo. Ho anche aggiornato la domanda. Anche l'app demo update4j presenta lo stesso problema. Grazie per la risposta!
palacsint,

1
30k + linee di log in un secondo è abbastanza grande ... non sei d'accordo? Mi chiedo davvero cosa potrebbe essere utile essere registrato per accettare questa elevata quantità di linee di registro in così poco tempo ... Hai provato a disattivare completamente la registrazione e creare il profilo dell'applicazione in questa modalità? (Sono curioso, ma forse la registrazione non ha davvero alcun impatto in quanto implica il comportamento di update4j)
Antares,

5

Dal momento che utilizza il 100% della CPU "la maggior parte del tempo" e impiega 10 volte di più (!) Con Java 14, significa che stai sprecando il 90% della tua CPU in Java 14.

A corto di spazio sul mucchio può farlo, poiché trascorri molto tempo in GC, ma sembra che tu l'abbia escluso.

Ho notato che stai modificando l'opzione di blocco parziale e che fa una differenza significativa. Questo mi dice che forse il tuo programma fa molto lavoro simultaneo su più thread. È possibile che il tuo programma abbia un bug di concorrenza che viene visualizzato in Java 14, ma non in Java 10. Ciò potrebbe anche spiegare perché l'aggiunta di un'altra CPU lo rende più del doppio più veloce.

Gli errori di concorrenza spesso vengono visualizzati solo quando si è sfortunati e il trigger potrebbe davvero essere stato qualsiasi cosa, come un cambiamento nell'organizzazione hashmap, ecc.

Innanzitutto, se è fattibile, controlla eventuali loop che potrebbero essere occupati in attesa invece di dormire.

Quindi, esegui un profiler in modalità di campionamento (lo farà jvisualvm) e cerca i metodi che richiedono una percentuale molto maggiore del tempo totale di quanto dovrebbero. Dal momento che la tua performance è decurtata di un fattore 10, qualsiasi problema in essa dovrebbe davvero saltare fuori.


Il blocco di parte era necessario in passato, ma al giorno d'oggi non così tanto, e si propone di essere disabilitato di default e successivamente rimosso: openjdk.java.net/jeps/374
JohannesB

3

TL; DR : è una regressione OpenJDK.

Non ho fatto eccezione, ma ho potuto riprodurre il problema con un semplice mondo ciao:

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello world");
    }
}

Ho usato questi due file batch:

main-1cpu.bat, che limita il javaprocesso a una sola CPU:

c:\windows\system32\cmd.exe /C start /affinity 1 \
    \jdk-14\bin\java \
    -Xlog:all=trace:file=app-1cpu.txt:uptime,tid,level,tags:filecount=50 \
    Main

main-full.bat, il javaprocesso può utilizzare entrambe le CPU:

c:\windows\system32\cmd.exe /C start /affinity FF \
    \jdk-14\bin\java \
    -Xlog:all=trace:file=app-full.txt:uptime,tid,level,tags:filecount=50 \
    Main

(Le differenze sono il affinityvalore e il nome del file di registro. L'ho spostato per una lettura più semplice ma il wrapping con \probabilmente non funziona su Windows.)

Alcune misure su Windows 10 x64 in VirtualBox (con due CPU):

PS Z:\main> Measure-Command { .\main-1cpu.bat }

...    
TotalSeconds      : 7.0203455
...


PS Z:\main> Measure-Command { .\main-full.bat }

...
TotalSeconds      : 1.5751352
...


PS Z:\main> Measure-Command { .\main-full.bat }

...
TotalSeconds      : 1.5585384
...


PS Z:\main> Measure-Command { .\main-1cpu.bat }

...
TotalSeconds      : 23.6482685
...

I tracelog prodotti contengono pause simili che puoi vedere nella domanda.

L'esecuzione Mainsenza tracelogs è più veloce, ma la differenza è ancora visibile tra la versione a CPU singola e versione a due CPU: ~ 4-7 secondi contro ~ 400 ms.

Ho inviato questi risultati all'elenco di posta hotspot-dev @ openjdk e gli sviluppatori lì hanno confermato che questo è qualcosa che il JDK potrebbe gestire meglio . Puoi trovare anche le supposte correzioni nel thread. Speriamo che venga risolto in OpenJDK 15.


2

Questo è un problema interessante e richiederebbe uno sforzo indeterminato per restringerlo poiché ci sono molte permutazioni e combinazioni che devono essere provate e dati raccolti e raccolti.

Sembra che non ci sia stata una soluzione a questo da qualche tempo. Forse questo potrebbe aver bisogno di essere intensificato.

EDIT 2: Poiché "ThreadLocalHandshakes" è obsoleto e possiamo supporre che il blocco sia conteso, suggerisci di provare senza "UseBiasedLocking" per accelerare questo scenario.

Tuttavia, ci sono alcuni suggerimenti per raccogliere più dati e tentare di isolare il problema.

  1. Assegna più di un core [Vedo che l'hai provato e il problema scompare. Sembra essere un problema con l'esecuzione di un thread che preclude gli altri. Vedi n. 7 sotto)
  2. Allocare più heap (forse le richieste di v14 sono superiori a quelle dei jdk precedenti)
  3. Alloca più memoria a Win 10 VB.
  4. Controlla i messaggi di sistema del sistema operativo (vinci 10 nel tuo caso)
  5. Eseguilo in una Win 10 non virtualizzata.
  6. Prova una build diversa di jdk 14
  7. Effettua un dump del thread ogni (o profilo) pochi intervalli di tempo. Analizza quale thread è in esecuzione esclusivamente. Forse esiste un'impostazione per un'equa ripartizione del tempo. Forse c'è un thread con priorità più alta in esecuzione. Cos'è quel thread e cosa sta facendo? In Linux è possibile stabilire in tempo reale i processi leggeri (thread) associati a un processo e al suo stato. Qualcosa di simile su Win 10?
  8. Uso della CPU? 100% o meno? Vincolato da CPU o mem? 100% CPU nei thread di servizio? Quale thread di servizio?
  9. Hai impostato esplicitamente un algoritmo GC?

Ho assistito personalmente a problemi all'interno delle versioni che hanno a che fare con GC, ridimensionamento dell'heap, problemi con contenitori virtualizzati e così via.

Non c'è una risposta facile a ciò, penso, soprattutto perché questa domanda è in circolazione da qualche tempo. Ma possiamo provare, tutto il meglio e farci sapere qual è il risultato di alcuni di questi passaggi di isolamento.

EDIT 1: dalla domanda aggiornata, sembra essere correlato a un GC o ad un altro thread di servizio che subentra in modo non equo al single core (Thread-Local Handshakes)?


Aggiunta di un core CPU aggiuntivo utilizzato per attivare il passaggio dall'ergonomia Java su sistemi a 32 bit da un client a una classe server vm con GC e compilazione a livelli diversi, se ciò è ancora possibile, potrebbe spiegare improvvise differenze nelle prestazioni e nell'uso della memoria, sì JVM le prestazioni sono complicate 😁
JohannesB,

3
L'ergonomia Java (impostazioni predefinite) è ancora diversa per 1 CPU (ad esempio: -XX: + UseSerialGC) o 2 CPU (ad esempio: G1GC, LoopStripMiningIter = 1000, ... ShortLoop = 100) Ma dopo essersi accertati di -XX: + PrintFlagsFinal che ho modificato tutti i parametri sullo stesso o simile update4j in esecuzione era ancora estremamente lento per iniziare con solo uno invece di 2 CPU con cmd.exe / C start / affinità 0x1 (ma estremamente veloce con 0x3 - quindi usando 2 cpus (1 + 10 binario)). Ho confermato che non possiamo dare la colpa a nessun garbage collector utilizzando Epsilon GC, progettato per evitare qualsiasi sovraccarico GC. Tuttavia, TieredCompilation è abilitato
JohannesB,

Vedo. Con Epsilon GC, sembra che sia stato così lento. In questo caso, gli stati dei thread e i dump per valutare dove si blocca potrebbe essere un modo. Sia nel mondo Java che nel mondo del sistema operativo (linux se ricordo fosse gcore)
Khanna111,

1

Fare attenzione con la registrazione per rallentare i dischi, rallenterà l'applicazione:

https://engineering.linkedin.com/blog/2016/02/eliminating-large-jvm-gc-pauses-caused-by-background-io-traffic

Ma non sembra essere la causa del problema poiché la CPU è ancora occupata e non devi aspettare che tutti i thread arrivino a un punto sicuro grazie all'handshake thread-local: https: // openjdk. java.net/jeps/312

Inoltre, non direttamente correlato al problema che hai, ma più in generale se vuoi provare a spremere più prestazioni dal tuo hardware per il tempo di avvio, dai un'occhiata a AppCDS (classe di condivisione dei dati):

https://blog.codefx.org/java/application-class-data-sharing/

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.