แอปพลิเคชั่นช้า JVM บ่อยครั้งค้างด้วยการตั้งค่า CPU เดี่ยวและ Java 12+


23

เรามีแอปพลิเคชันไคลเอนต์ (พร้อมการพัฒนามากกว่า 10 ปี) JDK มันถูกอัพเกรดจาก OpenJDK 11 เป็น OpenJDK 14 เมื่อเร็ว ๆ นี้ ใน CPU ตัวเดียว (ปิดการใช้งานไฮเปอร์เธรด) การตั้งค่า Windows 10 (และภายในเครื่อง VirtualBox ที่มี CPU เพียงตัวเดียวเท่านั้น) แอปพลิเคชั่นเริ่มช้าลงเมื่อเทียบกับ Java 11 นอกจากนี้ยังใช้ CPU 100% เกือบตลอดเวลา เราสามารถสร้างปัญหาขึ้นมาใหม่ได้ด้วยการตั้งค่าความสัมพันธ์ของตัวประมวลผลให้มีเพียงหนึ่ง CPU (c:\windows\system32\cmd.exe /C start /affinity 1 ... )

การวัดบางอย่างด้วยการเริ่มต้นแอปพลิเคชันและทำแบบสอบถามด้วยการโต้ตอบด้วยตนเองน้อยที่สุดในเครื่อง VirtualBox ของฉัน:

  • OpenJDK 11.0.2: 36 วินาที
  • OpenJDK 13.0.2: ~ 1.5 นาที
  • OpenJDK 13.0.2 ด้วย -XX:-UseBiasedLocking : 46 วินาที
  • OpenJDK 13.0.2 ด้วย -XX:-ThreadLocalHandshakes : 40 วินาที
  • OpenJDK 14: 5-6 นาที
  • OpenJDK 14 ด้วย -XX:-UseBiasedLocking : 3-3,5 นาที
  • OpenJDK 15 EA สร้าง 20: ~ 4,5 นาที

มีการเปลี่ยนแปลงเฉพาะ JDK ที่ใช้ (และตัวเลือกที่กล่าวถึง) (-XX:-ThreadLocalHandshakesไม่มีใน Java 14)

เราได้ลองบันทึกสิ่งที่ JDK 14 ทำด้วย -Xlog:all=debug:file=app.txt:uptime,tid,level,tags:filecount=50จะมี

การนับบรรทัดบันทึกสำหรับทุก ๆ วินาทีดูเหมือนจะค่อนข้างราบรื่นด้วย 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

ในทางตรงกันข้าม OpenJDK 14 ดูเหมือนจะมีช่วงเวลาที่เงียบสงบที่น่าสนใจ:

$ 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

แล้วเกิดอะไรขึ้นระหว่างวินาทีที่ 1-4, 7-10 และ 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
...

วินาทีหยุดชั่วคราวในภายหลัง:

...
[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
...

จากนั้นหนึ่งในสาม:

...
[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)
...

สองบรรทัดต่อไปนี้น่าสนใจ:

[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

เป็นเรื่องปกติที่การจับมือกันเหล่านี้ใช้เวลา 5.5 และ 6.8 วินาทีหรือไม่

ฉันเคยประสบปัญหาการชะลอตัว (และบันทึกที่คล้ายกัน) เหมือนกันกับแอปสาธิตของ update4j (ซึ่งไม่เกี่ยวข้องกับแอปพลิเคชันของเรา) ที่ทำงานด้วยคำสั่งนี้:

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

สิ่งที่ฉันควรมองหาเพื่อทำให้แอปของเราเร็วขึ้นอีกครั้งในการตั้งค่า CPU เดี่ยว Windows 10 ฉันสามารถแก้ไขได้โดยการเปลี่ยนบางสิ่งในแอปพลิเคชันของเราหรือโดยการเพิ่มข้อโต้แย้ง JVM?

นั่นเป็นข้อผิดพลาด JDK ฉันควรรายงานหรือไม่

อัพเดท 2020-04-25:

เท่าที่ฉันเห็นไฟล์บันทึกยังมีบันทึก GC นี่คือบันทึก 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)
...

น่าเสียดายที่มันดูเหมือนจะไม่เกี่ยวข้องกันตั้งแต่เริ่มหลังจากหยุดชั่วคราวครั้งที่สาม

อัปเดต 2020-04-26:

ด้วย OpenJDK 14 แอปพลิเคชันใช้ CPU 100% ในเครื่อง VirtualBox ของฉัน (single-CPU) (ทำงานบน i7-6600U CPU) เครื่องเสมือนมี RAM 3,5 GB ตาม Task Manager 40% + ฟรีและกิจกรรมของดิสก์คือ 0% (ฉันเดาว่านี่หมายถึงไม่มีการสลับ) การเพิ่มซีพียูอื่นไปยังเครื่องเสมือน (และเปิดใช้งานการทำไฮเปอร์เธรดสำหรับเครื่องทางกายภาพ) ทำให้แอปพลิเคชันนั้นเร็วพออีกครั้ง ฉันแค่สงสัยว่ามันเป็นการแลกเปลี่ยนโดยเจตนาระหว่างการพัฒนา JDK เพื่อลดประสิทธิภาพของเครื่องซีพียูเดี่ยว (หายาก) เพื่อให้ JVM เร็วขึ้นสำหรับซีพียูมัลติคอร์ / ไฮเปอร์เธรด


3
ไม่-Xlog:all=debugเปิด GC เข้าสู่ระบบ? นั่นเป็นครั้งแรกที่ฉันเดาว่าจะหยุดสักพัก
kichik

คุณลองใช้กับ profiler และเปรียบเทียบผลลัพธ์หรือไม่ ฉันคิดว่ามันคงเป็นเรื่องธรรมชาติที่ต้องทำ
Axel

1
ตรวจสอบข้อความในระบบ windows ลอง build ที่แตกต่างกันสำหรับ jdk 14. ถ้าทุกอย่างล้มเหลวให้เพิ่มเป็นปัญหาหรือไม่
Khanna111

1
@ Yan.F: OpenJDK 11 จะไม่ได้รับการสนับสนุนตลอดไปถึงเวลาเตรียมรุ่นใหม่และข้อบกพร่อง นอกจากนี้ดูเหมือนว่าข้อผิดพลาด JDK - ซึ่งอาจได้รับการแก้ไขหรือไม่ แต่สามารถช่วยเหลือผู้อื่นได้เช่นกัน อย่างไรก็ตามสำหรับฉันมันเป็นส่วนใหญ่อยากรู้อยากเห็น ในอีกด้านหนึ่งฉันต้องการบอกสิ่งที่ลูกค้าของเราต้องการให้เป็นระบบขั้นต่ำของแอพของเรา
palacsint

1
@ Khanna111: ใช่ฉันเพิ่งเขียนมันเป็นคำตอบ
palacsint

คำตอบ:


6

จากปัญหาประสิทธิภาพประสบการณ์ของฉันกับ JDK นั้นส่วนใหญ่เกี่ยวข้องกับหนึ่งในสิ่งต่อไปนี้:

  • รวบรวม JIT
  • การกำหนดค่า VM (ขนาดฮีป)
  • อัลกอริทึม GC
  • การเปลี่ยนแปลงใน JVM / JDK ซึ่งทำให้ทราบว่าแอปพลิเคชันทำงานได้ดีหรือไม่
  • (โอ้และฉันลืมพูดถึงการโหลดคลาส ... )

หากคุณเพิ่งใช้การกำหนดค่า JVM เริ่มต้นตั้งแต่ OpenJDK11 บางทีคุณควรตั้งค่าตัวเลือกที่โดดเด่นให้เป็นค่าคงที่เช่น GC ขนาดฮีปเป็นต้น

บางทีเครื่องมือวิเคราะห์แบบกราฟิกอาจช่วยติดตามปัญหาของคุณได้ Like Retrace, AppDynamics หรือ FlightRecorder และอื่น ๆ สิ่งเหล่านี้ให้ภาพรวมเพิ่มเติมเกี่ยวกับสถานะโดยรวมของ heap, gc cycles, RAM, เธรด, โหลด CPU และอื่น ๆ ในเวลาที่กำหนดมากกว่าที่ไฟล์บันทึกสามารถให้

ฉันเข้าใจอย่างถูกต้องหรือไม่ว่าแอปพลิเคชันของคุณเขียนประมาณ 30710 บรรทัดลงในบันทึกภายในวินาทีแรกของการทำงาน (ภายใต้ OpenJDK11) เพราะเหตุใดจึงเป็น "เพียง" เขียนประมาณ 7k บรรทัดภายใต้ OpenJDK14 ในวินาทีแรก? ดูเหมือนว่าจะมีความแตกต่างอย่างมากสำหรับแอปพลิเคชันที่เพิ่งเริ่มต้นบน JVM ที่แตกต่างกันสำหรับฉัน ... คุณแน่ใจหรือว่าไม่มีตัวอย่างของ stacktraces Exception จำนวนมากที่ถูกทิ้งลงในบันทึก
บางครั้งตัวเลขอื่น ๆ ก็ยิ่งสูงขึ้นดังนั้นการชะลอตัวอาจเกี่ยวข้องกับการบันทึกข้อยกเว้น? หรือแม้แต่การแลกเปลี่ยนหาก RAM เหลือน้อย
ที่จริงฉันคิดว่าถ้าแอปพลิเคชันไม่ได้เขียนอะไรลงในบันทึกนี่เป็นสัญญาณของการทำงานที่ราบรื่นโดยไม่มีปัญหา (เว้นแต่จะถูกแช่แข็งทั้งหมดในเวลานี้) สิ่งที่เกิดขึ้นตั้งแต่วินาทีที่ 12-22 (ในกรณี OpenJDK14 ที่นี่) คือสิ่งที่จะทำให้ฉันกังวลมากขึ้น ... สายที่บันทึกผ่านหลังคา ... ทำไม ?
และหลังจากนั้นการบันทึกจะลดลงทุกครั้งที่มีค่าต่ำประมาณ 1-2k บรรทัด ... อะไรคือสาเหตุของสิ่งนั้น ? (ดีบางทีมันเป็น gc kicking ในวันที่ 22 ที่สองและตาราง rasa ที่แก้ไขบางสิ่งบางอย่าง ... ?)

อีกอย่างอาจเป็นคำสั่งของคุณเกี่ยวกับเครื่อง "CPU เดี่ยว" นี่หมายถึง "single core" หรือไม่ (Idk ซอฟต์แวร์ของคุณอาจถูกปรับแต่งให้เหมาะกับฮาร์ดแวร์รุ่นเก่าหรือบางอย่าง) และ "single CPU" VMs กำลังทำงานบนเครื่องเหล่านั้นหรือไม่ แต่ฉันคิดว่าฉันผิดเกี่ยวกับสมมติฐานเหล่านี้เนื่องจากซีพียูเกือบทั้งหมดเป็นมัลติคอร์ทุกวันนี้ ... แต่ฉันจะตรวจสอบปัญหามัลติเธรด (การหยุดชะงัก)


2
โปรดอย่าใช้ลายเซ็นหรือแท็กไลน์ในโพสต์ของคุณการทำซ้ำ "GL และ HF" ถือว่าเป็นเสียงรบกวนและสิ่งที่ทำให้ไขว้เขวจากเนื้อหาของโพสต์ของคุณที่นี่ ดูmeta.stackexchange.com/help/behaviorสำหรับข้อมูลเพิ่มเติม
meagar

1
"ฉันเข้าใจถูกต้องหรือไม่ว่าแอปพลิเคชันของคุณเขียนประมาณ 30710 บรรทัดลงในบันทึกภายในวินาทีแรกของการทำงาน (ภายใต้ OpenJDK11)" - ใช่คุณพูดถูก.
palacsint

1
"คุณแน่ใจหรือว่าไม่มีตัวอย่างจำนวนมากที่กองการยกเว้นถูกทิ้งลงในบันทึกหรือไม่" - บันทึกนั้นสะอาดฉันไม่พบสิ่งแปลก ๆ ที่นั่นแอปพลิเคชันทำงานอย่างถูกต้อง (ยกเว้นว่ามันทำงานช้ามาก)
palacsint

1
GC กำลังเตะใน 22 วินาทีและแอปยังคงช้าหลังจากนั้น ฉันได้อัปเดตคำถามเช่นกัน โปรดทราบว่าแอปสาธิต update4j ยังมีปัญหาเดียวกัน ขอบคุณสำหรับคำตอบ!
palacsint

1
30k + บันทึกบรรทัดในหนึ่งวินาทีนั้นค่อนข้างใหญ่ ... คุณไม่เห็นด้วยไหม ฉันสงสัยจริงๆว่ามีประโยชน์อะไรบ้างที่จะถูกบันทึกไว้เพื่อยอมรับบรรทัดบันทึกจำนวนมากในช่วงเวลาสั้น ๆ ... คุณลองปิดการบันทึกอย่างสมบูรณ์และตั้งแอปพลิเคชั่นในโหมดนี้หรือไม่? (ฉันอยากรู้ แต่การบันทึกอาจไม่มีผลกระทบอย่างที่คุณบอกเป็นนัยกับพฤติกรรมของ update4j)
Antares

5

เนื่องจากใช้ซีพียูเกือบ 100% และใช้เวลานานกว่ากับ Java 14 ถึง 10 เท่าหมายความว่าคุณเสีย CPU 90% ใน Java 14

พื้นที่ว่างของฮีพสามารถทำเช่นนั้นได้ในขณะที่คุณใช้เวลาส่วนใหญ่ใน GC แต่ดูเหมือนว่าคุณจะได้จัดการกับปัญหานั้นแล้ว

ฉันสังเกตว่าคุณกำลังปรับแต่งตัวเลือกการล็อคแบบเอนเอียงและมันสร้างความแตกต่างที่สำคัญ นั่นบอกฉันว่าโปรแกรมของคุณอาจทำงานพร้อมกันหลายเธรด เป็นไปได้ว่าโปรแกรมของคุณมีบั๊กที่เกิดขึ้นพร้อมกันซึ่งแสดงใน Java 14 แต่ไม่ใช่ใน Java 10 ซึ่งอาจอธิบายได้ว่าทำไมการเพิ่ม CPU อื่นทำให้เร็วกว่าสองเท่า

ข้อบกพร่องที่เกิดขึ้นพร้อมกันมักจะปรากฏขึ้นเฉพาะเมื่อคุณโชคไม่ดีและตัวกระตุ้นอาจเป็นอะไรก็ได้เช่นการเปลี่ยนแปลงองค์กร hashmap เป็นต้น

ขั้นแรกถ้าเป็นไปได้ให้ตรวจสอบลูปใด ๆ ที่อาจกำลังรอแทนการนอน

จากนั้นรันโปรแกรมสร้างโปรไฟล์ในโหมดการสุ่มตัวอย่าง (jvisualvm จะทำ) และมองหาวิธีการที่ใช้เวลามากขึ้นกว่าที่ควรจะเป็น เนื่องจากประสิทธิภาพของคุณลดลง 10 เท่าปัญหาใด ๆ ในนั้นจึงควรกระโดดออกไป


การล็อกแบบเอนเอียงเป็นสิ่งจำเป็นในอดีต แต่ปัจจุบันไม่มากนักและจะเสนอให้ปิดใช้งานโดยค่าเริ่มต้นและถูกเอาออกในภายหลัง: openjdk.java.net/jeps/374
JohannesB

2

นี่เป็นปัญหาที่น่าสนใจและต้องใช้ความพยายามอย่าง จำกัด เพื่อ จำกัด ให้แคบลงเนื่องจากมีการเรียงสับเปลี่ยนและชุดค่าผสมจำนวนมากที่ต้องลองใช้และรวบรวมข้อมูลและเรียงลำดับ

ดูเหมือนว่า ณ ขณะนี้ยังไม่ได้มีการลงมติใด ๆ บางทีนี่อาจจำเป็นต้องเพิ่มขึ้น

แก้ไข 2: เนื่องจาก "ThreadLocalHandshakes" เลิกใช้แล้วและเราสามารถสันนิษฐานได้ว่าการล็อกนั้นมีการโต้แย้งขอแนะนำให้ลองโดยไม่ต้องใช้ "UseBiasedLocking" เพื่อเร่งความเร็วให้กับสถานการณ์นี้

อย่างไรก็ตามมีคำแนะนำในการรวบรวมข้อมูลเพิ่มเติมและพยายามแยกปัญหา

  1. จัดสรรมากกว่าหนึ่งคอร์ [ฉันเห็นว่าคุณลองแล้วและปัญหาหายไป ดูเหมือนว่าจะมีปัญหากับการดำเนินการเธรด / การดักคอผู้อื่น ดูหมายเลข 7 ด้านล่าง)
  2. จัดสรรฮีปให้มากขึ้น (บางทีความต้องการของ v14 นั้นมากกว่าของ jdks ก่อนหน้านี้)
  3. จัดสรรหน่วยความจำเพิ่มเติมให้กับ Win 10 VB
  4. ตรวจสอบข้อความระบบปฏิบัติการ (ชนะ 10 ในกรณีของคุณ)
  5. เรียกใช้ใน Win 10 ที่ไม่มีการจำลองเสมือน
  6. ลองสร้าง jdk รุ่นอื่น 14
  7. ทำเธรดดัมพ์ทุกช่วงเวลา (หรือโปรไฟล์) สองสามครั้ง วิเคราะห์ว่าเธรดใดกำลังทำงานเฉพาะ อาจมีการตั้งค่าสำหรับการแบ่งปันเวลาอย่างเท่าเทียมกัน อาจมีเธรดลำดับความสำคัญสูงกว่ากำลังทำงานอยู่ เธรดนั้นคืออะไรและกำลังทำอะไร ใน linux คุณสามารถจัดทำโพรเซสน้ำหนักเบา (เธรด) ที่เกี่ยวข้องกับกระบวนการและสถานะในเรียลไทม์ สิ่งที่คล้ายกับ Win 10 หรือไม่?
  8. การใช้งาน CPU หรือไม่ 100% หรือน้อยกว่า จำกัด โดย CPU หรือ mem? CPU 100% ในเธรดบริการหรือไม่ เธรดบริการใด
  9. คุณตั้งค่า GC algo อย่างชัดเจนหรือไม่

ฉันได้เห็นปัญหาส่วนตัวภายในรุ่นที่เกี่ยวข้องกับ GC การปรับขนาดฮีปปัญหาเกี่ยวกับคอนเทนเนอร์เสมือนจริงและอื่น ๆ

ไม่มีคำตอบที่ง่ายสำหรับฉันฉันคิดว่าโดยเฉพาะอย่างยิ่งเนื่องจากคำถามนี้ได้รับรอบระยะเวลาหนึ่ง แต่เราสามารถลองได้ดีที่สุดและแจ้งให้เราทราบว่าผลลัพธ์ของขั้นตอนการแยกเหล่านี้คืออะไร

แก้ไข 1: จากคำถามที่อัปเดตดูเหมือนว่าจะเกี่ยวข้องกับ GC หรือเธรดบริการอื่นที่ควบคุมแกนเดี่ยวที่ไม่เท่าเทียมกัน (Thread-Local Handshakes) หรือไม่


การเพิ่มซีพียูคอร์พิเศษที่ใช้เพื่อทริกเกอร์สวิตช์จากการยศาสตร์ของจาวาบนระบบ 32 บิตจากไคลเอนต์ไปยังเซิร์ฟเวอร์คลาส vm ด้วย GC ที่แตกต่างกันและการรวบรวมฉัตรหากเป็นกรณีนั้นอาจอธิบายความแตกต่างอย่างฉับพลันในประสิทธิภาพและการใช้หน่วยความจำใช่ ประสิทธิภาพค่อนข้างซับซ้อน😁
JohannesB

3
Java ergonomics (การตั้งค่าเริ่มต้น) ยังคงแตกต่างกันสำหรับ 1 CPU (เช่น: -XX: + UseSerialGC) หรือ 2 CPU (เช่น: G1GC, LoopStripMiningIter = 1000, ... ShortLoop = 100) แต่หลังจากตรวจสอบด้วย -XX: + PrintFlags ขั้นสุดท้ายที่ฉัน tweaked พารามิเตอร์ทั้งหมดเพื่อ update4j ทำงานเดียวกันหรือคล้ายกันก็ยังช้ามากที่จะเริ่มต้นด้วยเพียงหนึ่งแทน 2 CPU ของกับ cmd.exe / C เริ่มต้น / ความสัมพันธ์ 0x1 (แต่เร็วมากด้วย 0x3 - ดังนั้นใช้ 2 ซีพียู (1 + 10 ไบนารี)) ฉันยืนยันว่าเราไม่สามารถตำหนิตัวเก็บขยะใด ๆ ได้โดยใช้ Epsilon GC ซึ่งออกแบบมาเพื่อหลีกเลี่ยงค่าใช้จ่าย GC การเปิดใช้งาน TieredCompilation แล้ว
JohannesB

ฉันเห็น. ด้วย Epsilon GC ดูเหมือนว่าจะช้า ในกรณีนี้สถานะของเธรดและการดัมพ์เพื่อประเมินตำแหน่งที่ติดอาจเป็นวิธี ทั้งในโลก Java และ OS โลก (linux ถ้าฉันจำได้คือ gcore)
Khanna111

2

TL; DR : มันคือการถดถอยแบบ OpenJDK

ฉันไม่ได้ยกเว้น แต่ฉันสามารถสร้างปัญหาขึ้นมาใหม่ด้วยโลกสวัสดีง่ายๆ:

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

ฉันใช้ไฟล์แบตช์ทั้งสองนี้แล้ว:

main-1cpu.batซึ่ง จำกัดjavaกระบวนการเพียงหนึ่ง 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ที่javaกระบวนการสามารถใช้ทั้งซีพียู:

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

(ความแตกต่างคือaffinityค่าและชื่อของล็อกไฟล์ฉันหุ้มไว้เพื่อให้อ่านง่ายขึ้น แต่การตัดด้วย\อาจไม่ทำงานบน Windows)

การวัดเล็กน้อยใน Windows 10 x64 ใน VirtualBox (ที่มี 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
...

tracelogs ที่ผลิตมีการหยุดที่คล้ายกันซึ่งคุณสามารถดูได้ในคำถาม

การทำงานMainโดยไม่มี tracelogs นั้นเร็วกว่า แต่ก็ยังเห็นความแตกต่างได้ระหว่างซีพียูเดี่ยวและซีพียูสองรุ่น: ~ 4-7 วินาทีเทียบกับ ~ 400 ms

ผมได้ส่งผลการวิจัยนี้ไปยังฮอตสปอต-dev @ รายการจดหมาย OpenJDKและ devs มีการยืนยันว่านี่คือสิ่งที่ JDK สามารถจัดการที่ดีขึ้น คุณสามารถค้นหาการแก้ไขที่ควรในเธรดด้วย หวังว่ามันจะได้รับการแก้ไขใน OpenJDK 15


1

ใช้ความระมัดระวังในการบันทึกเพื่อให้ดิสก์ช้าลงแอปพลิเคชันของคุณจะช้าลง:

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

แต่ดูเหมือนจะไม่เป็นสาเหตุของปัญหาเนื่องจาก CPU ยังไม่ว่างและคุณไม่ต้องรอให้เธรดทั้งหมดมาถึงจุดที่ปลอดภัยด้วยการจับมือกับเธรดโลคัล: https: // openjdk java.net/jeps/312

ยังไม่เกี่ยวข้องโดยตรงกับปัญหาที่คุณมี แต่โดยทั่วไปถ้าคุณต้องการที่จะบีบประสิทธิภาพออกจากฮาร์ดแวร์ของคุณสำหรับเวลาเริ่มต้นให้ดูที่ AppCDS (การแบ่งปันข้อมูลระดับ):

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.