Tulis terlebih dahulu: tumpukan utas harus menjadi cara yang paling efektif untuk menemukan masalah non-fungsional dari aplikasi multi-threaded, dan dapat dikatakan sebagai senjata pembunuh. Tumpukan utas paling baik dalam menganalisis jenis masalah berikut:
Sistem ini memiliki CPU yang terlalu tinggi tanpa alasan.
Sistem ditangguhkan, tidak ada respons.
Sistem berjalan lebih lambat dan lebih lambat.
Kemacetan kinerja (seperti ketidakmampuan untuk sepenuhnya memanfaatkan CPU, dll.)
Thread Deadlock, Dead Loop, Starvation, dll.
Sistem gagal karena terlalu banyak utas (seperti ketidakmampuan untuk membuat utas, dll.).
Bagaimana menafsirkan tumpukan utas
Seperti yang ditunjukkan dalam program kode sumber Java berikut:
Paket org.ccgogoing.study.stacktrace;/** * @author: luochong400 * @description: Uji utas * @Date: Buat pada pukul 07:27 PM 2017/12/08 */kelas publik mytest {objek obj1 = objek baru (); Objek obj2 = objek baru (); public void fun1 () {disinkronkan (obj1) {fun2 (); }} public void fun2 () {disinkronkan (obj2) {while (true) {// Untuk mencetak tumpukan, analisis stack fungsi tidak keluar dari system.out.print (""); }} public static void main (string [] args) {mytest aa = mytest baru (); aa.fun1 (); }}Jalankan program di Ide, lalu tekan tombol CTRL+Break untuk mencetak informasi tumpukan utas sebagai berikut:
Benang penuh dump hotspot java (TM) server 64-bit VM (mode campuran 24.79-B02): "utas servis" daemon prio = 6 tid = 0x0000000000c53b000 nid = 0xca58 runnable [0x0000000000000000] java.Lang.thread.state: runemon: runemon: runememon "runemon1" compread "compread" compread "compread" compio "compio" compio. " tid = 0x000000000c516000 nid = 0xd390 menunggu kondisi [0x0000000000.000] java.lang.thread.state: runnable "c2 compilerthread0" Daemon prio = 10 tid = 0x000000.000c515000 nid = 0xcbac Waiting on condition [0x000000000000.000 java.lang.thread.state: runnable "monitor ctrl-break" daemon prio = 6 tid = 0x000000000c514000 nid = 0xd148 runnable [0x0000000Caee000] java.langstread.state: runnable at java.net.socketstread.state: runnable at java.neet.socketstreutstreMer0 java.net.socketInputStream.read (socketInputStream.java:152) di java.net.socketinputStream.read (socketinputStream.java:122) di sun.nio.cs.streamdecoder.readbytes (streamDecoder.jave:sstreamdecoder.readbytes (streamDecoder.jave:sstreamdecoder.readbytes (streamDecoder.jave:sstreamdecoder.readbytes (streamDecoder.jave:sstreamdecoder.readbytes (streamDecoder.jave:sstreamDoder.readbytes (streamDecoder.jave:sstreamdecoder.readbytes (streamDecoder.jave:) sun.nio.cs.streamdecoder.implread (streamDecoder.java:325) di sun.nio.cs.streamDecoder.read (streamDecoder.java:177) - terkunci <0x000000d7858b50> (a java.io.inputstream java.io.inputStreamReader.read (inputStreamreader.java:184) di java.io.bufferedreader.fill (bufferedreader.java:154) di java.io.bufferedreader.readline (bufferedreader.java.317) -buffereder.readline (bufferedreader.java:317) -buffereder.readline (bufferedreader.java:317) -buffereder. java.io.inputStreamReader) di java.io.bufferedreader.readline (bufferedreader.java:382) di com.intellij.rt.execution.application.appmainv2 $ 1.run (appmainv2.java:64) "attacher" daemon "daemon = 000000000000000000000000000000000000000000000000000000000000000000000000000angkitu nid = 0xd24c runnable [0x0000000000000000] java.lang.thread.State: runnable "Dispatcher" Daemon Prio = 10 tid = 0x0000000000C1A8800 NID = 0xD200 Tunggu [0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 TID = 0x0000000000ACE6000 NID = 0xCD74 di Object.wait () [0x000000000c13f000] java.lang.thread.State: menunggu (pada monitor objek) di java.lang.object.wait (metode asli) - tunggu di <0x000000000d728484888 (Metode Aasional) - Tunggu di <0x000000000d72848888888888888888888888888888888888888810 java.lang.ref.referencequeue.remove (ReferenceQueue.java:135) - Terkunci <0x000000d7284858> (A java.lang.ref.referencequeue $ lock) di java.lang.ref.referencequee.removeueeeUeeeeeeeeeeeeeeeeeee java.lang.ref.finalizer $ finalzerThread.run (finalizer.java:209) "Reference Handler" Daemon Prio = 10 TID = 0x00000000ACE4800 NID = 0XCE34 Di objek.Wait () [0x0000000000BF4F000] JAVA.THREAD.THREADE.THREADE.THREADE (ONTREADE (ONTHEADE (ONTREADE (ONTREADE (ONTREADE (ONTREAD: OPBOAD (0X00000000BF4F000] java.lang.object.wait (metode asli) - Menunggu <0x000000d7284470> (a java.lang.ref.reference $ lock) di java.lang.object.wait (objek.java:503) di java.lang.ref.reference $ refercerier.java:503) di java.lang.ref.reference $ refercerier.java:503) di java.lang.ref.reference $ refercerie.reference.reference.reference.reference.reference.reference.reference.reference. <0x00000000d7284470> (a java.lang.ref.reference $ lock) "main" prio = 6 tid = 0x0000000000238e800 nid = 0xc940 runnable [0x000000000 ATT org.ccgogoing.study.stacktrace.mytest.fun2 (mytest.java:22) - terkunci <0x000000d77d50c8> (a java.lang.object) di org.ccgogoing.study.stacktrace.mytest.funest.funest.fun15 (stackest.funest.funest.funest.funest.fun1 <0x00000000D77D50B8> (A java.lang.Object) di org.ccgogoing.study.stacktrace.mytest.main (mytest.java:29) "prio -prio = 10 running" goLNAGE = 0x0000000ACE1000 NID = 0xd0d0a8 Prio = 10 runn "goLNAGE = 0x0000000ACE#NID = 0xD0A8 PRIO8" TID = 0x000000000023A4000 NID = 0xD398 Runnable "GC Tugas Thread#1 (paralelgc)" prio = 6 tid = 0x00000000023a5800 nid = 0xcc20 runnable "TUGAS GC#2 (parallelgc)" prio = 6 tid = 0xble "rungnable" GC TUGAS#2 (parallelgc) "prio = 6 tid = 0xblable" GC TUGAS#2 (parallelgc) "prio = 6 tid = 0xble" rungnable "gc#2 (parallelgc)" prio = 6 tid = 0xble "rungnable" GC#2 (parallelgc) "prio = 6 tid = 0xble" rungnable# "Tugas Tugas GC#3 (paralelgc)" prio = 6 tid = 0x00000000023a9000 nid = 0xd088 runnable "vm utas periodik" prio = 10 tid = 0x000000000c53f000 nid = 0xc1b4 Tunggu pada kondisi JNI Global: 138Heap.000 psy = 0xc1b4 Tunggu pada kondisi JNI Global: 138Heap.000 psy = 0xc1b4 Tunggu pada kondisi JNI Global: 138Heap.000 psy = 0xc1b4 Referensi JNI Global: 138Heap.000 [0x000000000D7280000, 0x00000000D7280000, 0x0000000D9B800000, 0x000000100000000) Ruang Eden 31744K, 20% Digunakan [0x0000000D7280000, 0x0000000D78BA0D0, 0x00000D7280000) DAR [0x000000000D9680000, 0x0000000D9680000, 0x0000000D9B80000) ke Space 5120k, 0% Digunakan [0x0000000D9180000, 0x0000000D9180000, 0x0000000D9180000, 0x0000000D9180000, 0x0000000D9180000, 0x00000D9180000 ) [0x0000000080600000000, 0x000000081B000000, 0x0000000085800000) Ruang objek 21504k, 15% Digunakan [0x0000000806000000, 0x00000000809404040404040404040404040 0x00000000080939290, 0x00000000081B000000)
Dalam output tumpukan di atas, kita dapat melihat bahwa ada banyak utas latar belakang dan utas utama, di antaranya hanya utas utama milik utas pengguna Java, dan yang lainnya secara otomatis dibuat oleh mesin virtual. Selama analisis kami, kami hanya peduli dengan utas pengguna.
Dari utas utama di atas, Anda dapat melihat konteks panggilan utas saat ini dengan cara yang sangat intuitif. Arti dari lapisan panggilan tertentu dari satu utas adalah sebagai berikut:
di mytest.fun1 (mytest.java:15) | | | | | | | | | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Selain itu, ada pernyataan di tumpukan:- terkunci <0x0000000d77d50b8> (java.lang.Object) yang menunjukkan bahwa utas sudah memiliki kunci <0x0000000d77d50b8>, dan braket sudut menunjukkan ID kunci. Ini secara otomatis dihasilkan oleh sistem. Kita hanya perlu tahu bahwa tumpukan dicetak setiap kali, dan ID yang sama berarti kunci yang sama. Baris pertama dari setiap tumpukan utas berarti sebagai berikut:
"Main" prio = 1 tid = 0x000000000238e800 nid = 0xc940 runnable [0x00000000027af000] | | | | | | | | | | | | | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Bahasa parsing dan entitas yang dieksekusi adalah mesin virtual Java, utas dalam bahasa Java yang dijalankan dengan utas lokal di mesin virtual, dan sebenarnya adalah utas lokal yang menjalankan kode utas Java.
Interpretasi kunci
Dilihat dari tumpukan utas di atas, informasi langsung yang terkandung dalam tumpukan utas adalah: jumlah utas, tumpukan metode yang dipanggil oleh masing -masing utas, dan status kunci saat ini. Jumlah utas dapat dihitung secara langsung; Tumpukan metode yang dipanggil oleh utas, dari bawah ke atas, berarti metode mana di kelas mana yang telah dipanggil oleh utas saat ini. Dan keadaan kunci tampaknya sedikit rumit. Informasi yang terkait dengan kunci adalah sebagai berikut:
Saat utas memiliki kunci, tumpukan utas akan mencetak -terkunci <0x00000000d77d50c8>
Saat utas menunggu utas lain untuk melepaskan kunci, tumpukan utas akan mencetak -waiting to lock <0x00000000d77d50c8>
Saat utas memiliki kunci, tetapi menjalankan metode tunggu () dari kunci, stack utas cetakan pertama terkunci, dan kemudian mencetak -waiting on
<0x00000000d77d50c8>
Interpretasi status utas
Dengan bantuan tumpukan utas, banyak jenis masalah dapat dianalisis, dan analisis konsumsi CPU adalah bagian penting dari analisis tumpukan utas;
Thread di timed_waiting dan status tunggu tidak boleh mengkonsumsi CPU. Untuk utas di Runnable, kita harus menilai apakah mereka mengonsumsi CPU berdasarkan sifat kode saat ini.
Jika itu adalah kode operasi Java murni, ia mengkonsumsi CPU.
Jika jaringan IO, jarang mengkonsumsi CPU.
Jika itu adalah kode lokal, Anda harus menilai berdasarkan sifat kode lokal (tumpukan utas lokal dapat diperoleh melalui pstack dan gStack). Jika itu adalah kode operasi murni, ia mengkonsumsi CPU. Jika ditangguhkan, itu tidak mengonsumsi CPU. Jika IO, itu tidak mengkonsumsi banyak CPU.
Bagaimana menganalisis masalah dengan tumpukan utas
Tumpukan utas sangat membantu dalam memposisikan pertanyaan dari jenis berikut:
Analisis kebuntuan utas
Analisis CPU berlebihan yang disebabkan oleh kode Java
Analisis Siklus Kekerasan
Analisis Sumber Daya yang Tidak Cukup
Analisis Bottleneck Kinerja
Analisis Deadlock Thread
Saya tidak akan menjelaskan terlalu banyak tentang konsep kebuntuan. Jika Anda tidak mengerti, Anda dapat memeriksanya secara online;
Cincin kunci yang dibentuk oleh dua atau lebih utas karena ketergantungan kunci dari loop membentuk kebuntuan yang sebenarnya, sebagai berikut:
Menemukan satu level Java Deadlock: ========================================================= =================================================================== =================================================================== =================================================================== =================================================================== =================================================================== =================================================================== =================================================================== Kunci Monitor 0x0000000000A9ABC78 (Objek 0x000000000D77363E0, A java.lang.Object), yang dipegang oleh "org.ccgogoing.study.stacktrace.deadlock.testthread2" Java Stack Informasi untuk threads yang terdaftar. Atas: ==================================================================================================================== ======================================================================================================================================================= ======================================================================================================================================================== ======================================================================================================================================================= org.ccgogoing.study.stacktrace.deadlock.testthread2.fun (testthread2.java:35) - Menunggu untuk mengunci <0x000000d77363d0> (a java.lang.object) - terkunci <0x000000d777363e0> (a. org.ccgogoing.study.stacktrace.deadlock.testthread1 ": at org.ccgogoing.study.stacktrace.deadlock.testthread1.fun (testthread1.java:33) - menunggu untuk mengunci <0x00000000d77363e0> (a locklection) (a. 0x000000d77363e0> (aRAVA). <0x00000000D77363D0> (A java.lang.Object) di org.ccgogoing.study.stacktrace.deadlock.testthread1.run (testthread1.java:20) menemukan 1 jalan buntu.
Dari tumpukan yang dicetak, kita dapat melihat "menemukan satu jalan buntu tingkat Java:", yaitu, jika ada situasi kebuntuan, tumpukan akan langsung memberikan hasil analisis kebuntuan.
Ketika satu set utas Java mati, itu berarti permainan berakhir, utas ini akan selalu digantung di sana dan tidak akan pernah bisa terus berjalan. Ketika utas yang memiliki kebuntuan menjalankan fungsi kritis sistem, kebuntuan ini dapat menyebabkan seluruh sistem lumpuh. Untuk memulihkan sistem, cara sementara dan satu -satunya untuk menghindarinya adalah dengan memulai kembali sistem. Kemudian cepat dan ubah bug yang menyebabkan kebuntuan ini.
CATATAN: Dua atau lebih utas yang menemui jalan buntu jangan mengkonsumsi CPU. Beberapa orang percaya bahwa tingkat penggunaan 100% CPU disebabkan oleh kebuntuan utas. Pernyataan ini benar -benar salah. Loop mati, dan kode dalam loop adalah CPU-intensif, yang dapat menyebabkan tingkat penggunaan 100% CPU. Operasi IO seperti soket atau basis data tidak mengkonsumsi banyak CPU.