เขียนมันก่อน: สแต็กเธรดควรเป็นวิธีที่มีประสิทธิภาพมากที่สุดในการค้นหาปัญหาที่ไม่ใช้งานของแอปพลิเคชันแบบมัลติเธรดและสามารถกล่าวได้ว่าเป็นอาวุธนักฆ่า สแต็คเธรดดีที่สุดในการวิเคราะห์ปัญหาประเภทต่อไปนี้:
ระบบมี CPU สูงเกินไปโดยไม่มีเหตุผล
ระบบถูกระงับไม่มีการตอบสนอง
ระบบทำงานช้าลงและช้าลง
คอขวดประสิทธิภาพ (เช่นการไม่สามารถใช้ประโยชน์จาก CPU ฯลฯ ได้อย่างเต็มที่)
ด้าย Deadlock, Dead Loop, ความอดอยาก ฯลฯ
ระบบล้มเหลวเนื่องจากเธรดมากเกินไป (เช่นไม่สามารถสร้างเธรด ฯลฯ )
วิธีตีความสแต็กเธรด
ดังที่แสดงในโปรแกรมซอร์สโค้ด Java ต่อไปนี้:
แพ็คเกจ org.ccgogoing.study.stacktrace;/** * @author: luochong400 * @description: ทดสอบเธรด * @date: สร้างใน 07:27 PM 2017/12/08 */คลาสสาธารณะ mytest {object obj1 = วัตถุใหม่ (); วัตถุ obj2 = วัตถุใหม่ (); โมฆะสาธารณะ fun1 () {ซิงโครไนซ์ (obj1) {fun2 (); }} โมฆะสาธารณะ fun2 () {ซิงโครไนซ์ (obj2) {ในขณะที่ (จริง) {// เพื่อพิมพ์สแต็กการวิเคราะห์สแต็กฟังก์ชั่นไม่ออกจาก System.out.print (""); }} โมฆะคงที่สาธารณะหลัก (สตริง [] args) {mytest aa = new mytest (); aa.fun1 (); -เรียกใช้โปรแกรมในแนวคิดจากนั้นกดปุ่ม Ctrl+Break เพื่อพิมพ์ข้อมูลสแต็กเธรดดังนี้:
Full Thread Dump Java Hotspot (TM) เซิร์ฟเวอร์ 64 บิต VM (24.79-B02 โหมดผสม): "เธรดบริการ" daemon prio = 6 tid = 0x0000000000c53b000 nid = 0xca58 runnable [0x00000000000000] Java.lang TID = 0x000000000C516000 NID = 0xd390 รอเงื่อนไข [0x000000000000000] java.lang.thread.state: runnable "C2 CompilerThread0" Daemon PRIO = 10 TID = 0x000000000C515000 NID = 0xCBAC java.lang.thread.state: runnable "จอภาพ ctrl-break" daemon prio = 6 tid = 0x000000000c514000 nid = 0xd148 runnable [0x000000000caee000] java.lang.Thread.state java.net.socketInputStream.read (socketInputStream.java:152) ที่ java.net.socketInputStream.read (socketInputStream.java:122) ที่ sun.nio.cs.cs.streamdecoder.readbytes sun.nio.cs.streamdecoder.implread (streamdecoder.java:325) ที่ sun.nio.cs.streamdecoder.read (streamdecoder.java:177) - ล็อค <0x000000d7858b50> java.io.inputStreamreader.read (inputstreamreader.java:184) ที่ java.io.bufferedreader.fill (bufferedreader.java:154) ที่ java.io.bufferedreader.readline (bufferedreader.java:317) java.io.inputStreamreader) ที่ java.io.bufferedreader.readline (bufferedreader.java:382) ที่ com.intellij.rt.execution.application.appmainv2 $ 1.Run (AppMainV2.Java:64) Runnable [0x0000000000000000] java.lang.thread.state: runnable "signal dispatcher" daemon prio = 10 tid = 0x0000000000c1a8800 nid = 0xd200 รอเงื่อนไข [0x00000000000000 tid = 0x0000000000ace6000 nid = 0xcd74 ใน object.wait () [0x000000000c13f000] java.lang.thread.state: รอ (บนจอภาพวัตถุ) ที่ java.lang.object.wait java.lang.ref.referencequeue.remove (ReferenceQueue.java:135) - ล็อค <0x000000d7284858> (a java.lang.ref.referencequeue $ ล็อค) ที่ java.lang.ref.referencequeue.romove java.lang.ref.finalizer $ finalizerThread.run (finalizer.java:209) "ตัวจัดการอ้างอิง" daemon prio = 10 tid = 0x00000000ace4800 nid = 0xce34 ใน object.wait () [0x0000000000bf4f000] Java.lang java.lang.object.wait (วิธีการดั้งเดิม) - รอ <0x000000d7284470> (a java.lang.ref.reference $ lock) ที่ java.lang.object.wait (Object.java:503) <0x00000000d7284470> (a java.lang.ref.reference $ lock) "หลัก" prio = 6 tid = 0x00000000238e800 nid = 0xc940 runnable [0x00000000027af000] java.lang org.ccgogoing.study.stacktrace.mytest.fun2 (mytest.java:22) - ล็อค <0x000000d77d50c8> (a java.lang.object) ที่ org.ccgoing.study.stacktrace.mytest.fun1 <0x00000000d77d50b8> (a java.lang.Object) ที่ org.ccgoGoing.study.stacktrace.mytest.main (mytest.java:29) "thread vm" prio = 10 tid = 0x000000000ACE1000 NID = 0xD0A TID = 0x000000000023A4000 NID = 0xD398 Runnable "GC Task Thread#1 (ParallelGc)" Prio = 6 TID = 0x00000000023A5800 NID = 0xCC20 Runnable "GC Task#2 (ParallelGc)" เธรด#3 (parallelgc) "prio = 6 tid = 0x00000000023a9000 nid = 0xd088 runnable" thread task periodic vm "prio = 10 tid = 0x000000000c53f000 nid = 0xc1b4 รอเงื่อนไข Jni การอ้างอิงทั่วโลก: 138Heap [0x000000000D7280000, 0x00000000D7280000, 0x0000000D9B800000, 0x000000100000000) EDEN Space 31744K, 20% ใช้ [0x000000000D7280000, 0x0000000D78BA0D0, 0x000000000 [0x000000000D9680000, 0x0000000D9680000, 0x0000000D9B80000) ถึงอวกาศ 5120K, 0% ใช้ [0x000000000D9180000, 0x0000000D9180000, 0x0000000D9180000 [0x000000000085800000, 0x0000000008A980000, 0x0000000D7280000) พื้นที่วัตถุ 83456K, 0% ใช้ [0x00000000085800000, 0x0000000085800000, 0x000000008a980000) [0x0000000000806000000, 0x000000081B000000, 0x0000000085800000) พื้นที่วัตถุ 21504K, 15% ใช้ [0x000000000806000000, 0x0000000080939290, 0x00000000000000000000 0x00000000080939290, 0x00000000081B000000)
ในเอาต์พุตสแต็กข้างต้นเราจะเห็นว่ามีเธรดพื้นหลังมากมายและเธรดหลักซึ่งมีเพียงเธรดหลักเท่านั้นที่เป็นของเธรดผู้ใช้ Java และส่วนอื่น ๆ จะถูกสร้างขึ้นโดยอัตโนมัติโดยเครื่องเสมือนจริง ในระหว่างการวิเคราะห์ของเราเราสนใจเฉพาะหัวข้อผู้ใช้เท่านั้น
จากเธรดหลักด้านบนคุณสามารถเห็นบริบทการโทรของเธรดปัจจุบันในวิธีที่ใช้งานง่ายมาก ความหมายของเลเยอร์ที่แน่นอนของการโทรของหนึ่งเธรดมีดังนี้:
ที่ mytest.fun1 (mytest.java:15) | - - - - - - - - - -
นอกจากนี้ยังมีคำสั่งในสแต็ก:- ล็อค <0x0000000d77d50b8> (java.lang.Object) ระบุว่าเธรดมีการล็อค <0x0000000d77d50b8> และวงเล็บมุมระบุรหัสล็อค สิ่งนี้ถูกสร้างขึ้นโดยอัตโนมัติโดยระบบ เราเพียงแค่ต้องรู้ว่าสแต็กที่พิมพ์ทุกครั้งและ ID เดียวกันหมายถึงการล็อคเดียวกัน บรรทัดแรกของแต่ละเธรดสแต็คมีความหมายดังนี้:
"หลัก" prio = 1 tid = 0x000000000238e800 nid = 0xc940 runnable [0x00000000027af000] | - - - - - - - - - - - - +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- การแยกวิเคราะห์ภาษาและเอนทิตีที่ดำเนินการเป็นเครื่องเสมือน Java เธรดในภาษา Java Run ด้วยเธรดท้องถิ่นในเครื่องเสมือนจริงและเป็นจริงเธรดท้องถิ่นที่ดำเนินการรหัสเธรด Java
การตีความล็อค
การตัดสินจากสแต็กเธรดด้านบนข้อมูลโดยตรงที่มีอยู่ในสแต็กเธรดคือ: จำนวนเธรด, เมธอดสแต็กที่เรียกโดยแต่ละเธรดและสถานะล็อคปัจจุบัน จำนวนเธรดสามารถนับได้โดยตรง สแต็ควิธีการที่เรียกโดยเธรดจากด้านล่างถึงด้านบนหมายถึงวิธีที่คลาสที่ถูกเรียกโดยเธรดปัจจุบัน และสถานะล็อคก็ดูยุ่งยากเล็กน้อย ข้อมูลที่เกี่ยวข้องกับการล็อคมีดังนี้:
เมื่อเธรดเป็นเจ้าของล็อคสแต็กของเธรดจะพิมพ์ -ล็อค <0x000000d77d50c8> ล็อค
เมื่อเธรดกำลังรอให้เธรดอื่นปล่อยล็อคสแต็กเธรดจะพิมพ์ -รอล็อค <0x000000d77d50c8>
เมื่อเธรดเป็นเจ้าของล็อค แต่ดำเนินการวิธีการรอ () ของล็อคสแต็กเธรดพิมพ์ครั้งแรกล็อคแล้วพิมพ์ -รออยู่
<0x00000000d77d50c8>
การตีความสถานะเธรด
ด้วยความช่วยเหลือของสแต็คเธรดปัญหาหลายประเภทสามารถวิเคราะห์ได้และการวิเคราะห์การบริโภค CPU เป็นส่วนสำคัญของการวิเคราะห์สแต็กเธรด
เธรดใน timed_waiting สถานะและการรอคอยจะต้องไม่ใช้ CPU สำหรับเธรดใน Runnable เราต้องตัดสินว่าพวกเขาบริโภค CPU ตามลักษณะของรหัสปัจจุบันหรือไม่
หากเป็นรหัสการทำงานของ Java บริสุทธิ์มันจะใช้ CPU
ถ้าเป็นเครือข่าย IO มันไม่ค่อยใช้ CPU
หากเป็นรหัสท้องถิ่นคุณควรตัดสินตามลักษณะของรหัสท้องถิ่น (สามารถรับสแต็กเธรดท้องถิ่นผ่าน PSTACK และ GSTACK) หากเป็นรหัสการทำงานที่บริสุทธิ์จะใช้ CPU หากถูกระงับจะไม่ใช้ CPU ถ้าเป็น IO มันไม่ได้กิน CPU มากนัก
วิธีวิเคราะห์ปัญหาด้วยสแต็กเธรด
สแต็คเธรดมีประโยชน์มากในการวางตำแหน่งคำถามประเภทต่อไปนี้:
การวิเคราะห์การหยุดชะงักของเธรด
การวิเคราะห์ CPU มากเกินไปที่เกิดจากรหัส Java
การวิเคราะห์วัฏจักรความรุนแรง
การวิเคราะห์ทรัพยากรไม่เพียงพอ
การวิเคราะห์คอขวดประสิทธิภาพ
การวิเคราะห์ Deadlock เธรด
ฉันจะไม่อธิบายมากเกินไปเกี่ยวกับแนวคิดเรื่องการหยุดชะงัก หากคุณไม่เข้าใจคุณสามารถตรวจสอบออนไลน์ได้
แหวนล็อคที่เกิดขึ้นโดยสองเธรดขึ้นไปเนื่องจากการพึ่งพาล็อคของลูปก่อให้เกิดการหยุดชะงักอย่างแท้จริงดังต่อไปนี้:
พบระดับชวาหนึ่งระดับ Deadlock: ================================================================================ - - - - - - - ล็อคจอมอนิเตอร์ 0x0000000000a9abc78 (วัตถุ 0x000000000d77363e0, java.lang.object) ซึ่งจัดขึ้นโดย "org.ccgoGoing.study.stacktrace.deadlock.testthread2" Java Stack Information ด้านบน: =========================================================================================================================== - - - org.ccgogoing.study.stacktrace.deadlock.testthread2.fun (testthread2.java:35) - รอล็อค <0x000000d77363d0> (java.lang.object) org.ccgogoing.study.stacktrace.deadlock.testthread1 ": ที่ org.ccgoGoing.study.stacktrace.deadlock.testthread1.fun (testthread1.java:33) - รอล็อค <0x00000000d77363d0> (a java.lang.Object) ที่ org.ccgoGoing.study.stacktrace.deadlock.testthread1.run (testthread1.java:20) พบ 1 Deadlock
จากสแต็คที่พิมพ์ออกมาเราจะเห็น "พบว่าการหยุดชะงักระดับชวาหนึ่งครั้ง:" นั่นคือถ้ามีสถานการณ์การหยุดชะงักสแต็กจะให้ผลการวิเคราะห์หยุดชะงักโดยตรง
เมื่อชุดของกระทู้ Java ตายมันหมายถึงเกมมากกว่าเธรดเหล่านี้จะถูกแขวนไว้ที่นั่นเสมอและจะไม่สามารถวิ่งต่อไปได้ เมื่อเธรดที่มีการหยุดชะงักกำลังดำเนินการฟังก์ชั่นที่สำคัญของระบบการหยุดชะงักนี้อาจทำให้ระบบทั้งหมดเป็นอัมพาต ในการกู้คืนระบบวิธีชั่วคราวและวิธีเดียวที่จะหลีกเลี่ยงได้คือการรีสตาร์ทระบบ จากนั้นรีบขึ้นและแก้ไขข้อผิดพลาดที่ทำให้เกิดการหยุดชะงักนี้
หมายเหตุ: สองเธรดขึ้นไปที่หยุดชะงักไม่ได้ใช้ CPU บางคนเชื่อว่าอัตราการใช้งาน 100% ของ CPU เกิดจากการหยุดชะงักของด้าย คำสั่งนี้ผิดอย่างสมบูรณ์ The Dead Loop และรหัสในลูปนั้นมีความเข้มข้นของ CPU ซึ่งอาจนำไปสู่อัตราการใช้งาน 100% ของ CPU การดำเนินการของ IO เช่นซ็อกเก็ตหรือฐานข้อมูลไม่ได้ใช้ CPU มากนัก