먼저 쓰기 : 스레드 스택은 다중 스레드 애플리케이션의 기능이없는 문제를 찾는 가장 효과적인 수단이어야하며 킬러 무기라고 할 수 있습니다. 스레드 스택은 다음 유형의 문제를 분석하는 데 가장 좋습니다.
시스템의 이유없이 CPU가 너무 높습니다.
시스템이 중단되며 응답이 없습니다.
시스템이 느리고 느리게 작동합니다.
성능 병목 현상 (예 : CPU를 완전히 활용할 수 없음 등).
스레드 교착 상태, 데드 루프, 기아 등
스레드가 너무 많기 때문에 시스템이 실패합니다 (예 : 스레드를 생성 할 수없는 등).
스레드 스택을 해석하는 방법
다음 Java 소스 코드 프로그램에 표시된대로 :
패키지 org.ccgogoing.study.stacktrace;/** * @author : luochong400 * @description : 테스트 스레드 * @date : 07:27 pm 2017/12/08 */public class mytest {object obj1 = new Object (); Object obj2 = new Object (); public void fun1 () {synchronized (obj1) {fun2 (); }} public void fun2 () {synchronized (obj2) {while (true) {// 스택을 인쇄하기 위해 함수 스택 분석은 System.out.print ( "")를 종료하지 않습니다. }} public static void main (String [] args) {mytest aa = new mytest (); aa.fun1 (); }}아이디어로 프로그램을 실행 한 다음 Ctrl+Break 키를 눌러 다음과 같이 스레드 스택 정보를 인쇄하십시오.
전체 스레드 덤프 Java Hotspot (TM) 64 비트 서버 VM (24.79-B02 혼합 모드) : "서비스 스레드"Daemon Prio = 6 TID = 0x0000000000C53B000 NID = 0XCA58 Runnable [0x00000000000000] java.lang.thread.state : runnable "Compilerthred11"DaMone PRION " TID = 0x000000000C516000 NID = 0XD390 조건에서 대기 대기 [0x00000000000000] Java.Lang.thread.state : runnable "C2 CompilerThread0"Daom Emon Prio = 10 TID = 0x00000000000c515000 NID = 0XCBAC 대기 조건 [0x00000000000000000000000] java.lang.thread.state : runnable "모니터 ctrl-break"daemon prio = 6 tid = 0xd148 runnable [0xd148 runnable [0x000000000caee000] java.lang.state : java.net.net.net.sukeinputstream.setchetcheactread0 at java.net. java.net.socketinputstream.read (java.net.socketinputstream.read에서 sun.nio.cs.streamdecoder.streamdecoder.readbytes (streamdecoder.java:283)의 java.net.socketinputstream.read (socketinputstream.java:152) sun.nio.cs.streamdecoder.implread (streamdecoder.mplread java.io.inputStreamReader.read (java.io.bufferedReader.fill에서 java.io.bufferedReader.Readline (bufferedReader.java:317)에서 java.io.bufferedReader.fill (inputStreamReader.java:184) - <0x00000d78588888888888888888888888888888888888888888B50> java.io.inputStreamReader) at java.io.bufferedReader.Readline (bufferedReader.java:382)의 com.intellij.rt.execution.application.appmainv2 $ 1.run (appmainv2.java:64) "appmmainv2.java:64)"첨부 청취자 "10 tid = 0x00000000004a000 NID = 0xD24C runnable [0x00000000000000] java.lang.thread.state : runnable "Signal Dispatcher"Daemon Prio = 10 tid = 0x00000000c1a8800 NID = 0xD200 대기 조건 [0x0000000000000000] [0x0000000000000000] java.lang.State "runnable" TID = 0x0000000000CACE6000 NID = 0xCD74 in Object.Wait () [0x00000000000C13F000] java.lang.theat.state : java.lang.state : java.lang.object.wait에서 대기 (객체 모니터에서 대기) - <0x00000000000d72848588>에서 대기 중). java.lang.ref.ref.reffere.remove (referencequeue.java:135) - 잠금 <0x000000d7284858> (java.lang.ref.referencequeue $ lock) at java.lang.ref.referenceque.remove (참조 Queue.java:151) at java.lang.ref.finalizer $ finalizerthread.run (finalizer.java:209) "참조 핸들러"Daemon Prio = 10 tid = 0x0000000000ace4800 NID = 0xce34 in object.wait () [0x0000000000bf4f000] java.lang.Thread.Thread.Thread.Thread.Thread.Thread.State : java.lang.object.wait (기본 메소드) - 대기 중 <0x00000000d7284470> (java.lang.ref.ref.reference $ lock) at java.lang.object.wait (object.java:503) at java.lang.ref.reference $ Reference.run (wefence.java:133) - Java:133). <0x00000000d7284470> (java.lang.ref.reference $ lock) "Main"PRIO = 6 TID = 0x000000000000238E800 NID = 0XC940 RUNNABLE [0x00000000027AF000] java.lang.thread.state : org.ccgogoing.study.stacktrace.mytest.fun2 (mytest.java:22) -Locked <0x00000000d77d50c8> (a java.lang.object) at org.ccgogoing.study.stacktrace.mytest.fun1 (mytest.java:15). <0x00000000d77d50b8> (org.ccgogoing.study.stacktrace.mytest.main (mytest.java:29)의 java.lang.object) "VM 스레드"prio = 10 tid = 0x000000000ace1000 nid = 0xd0a8 runbable "GC Task#0 (waste#0) TID = 0x000000000023A4000 NID = 0xD398 RUNNABLE "GC TASKSTRED#1 (PARIMEGGC)"PRIO = 6 TID = 0x00000000023A5800 NID = 0XCC20 RUNNABLE "GC Task Thread (PAILLEGC)"PRAILLGC = 6 TID = 0X00000000023000 NID = 0XB914 스레드#3 (ParemalGC) "PRIO = 6 TID = 0x00000000023A9000 NID = 0XD088 RUNNABLE"VM 주기성 작업 스레드 "PRIO = 10 TID = 0X000000000C53F000 NID = 0XC1B4 조건 JNI 글로벌 참조 : 138HEAP PSYANGGGEN 총 36864K, 6376K 사용 6376K [0x0000000000d7280000, 0x000000d7280000, 0x0000000D9B800000, 0x0000001000000) Eden Space 31744K, 20% 사용 된 [0x0000000d7280000, 0x00000000d78ba0d0, 0x000000000000). [0x000000000D9680000, 0x0000000D9680000, 0x000000000D9B80000) 우주 5120K, 0% 사용 [0x00000000000D9180000, 0x0000000D9180000, 0x000000000D9180000, 0x0000000D9680000) Paroldgen. [0x000000000085800000, 0x0000000008A980000, 0x0000000D7280000) 객체 공간 83456K, 0% 사용 [0x0000000000085800000, 0x00000085800000, 0x000000008A980000) PSPERMGEN 총 2150K, [0x0000000000806000000, 0x000000081B000000, 0x0000000085800000) 객체 공간 21504K, 15% 사용 [0x00000000000806000000, 0x00000080939290, 0x0000000081B00000000000000000000000000000000000000000000000000000000000004K 사용 0x0000000000080939290, 0x00000000081B000000)
위의 스택 출력에서는 많은 배경 스레드와 메인 스레드가 있으며 그중에는 주 스레드 만 Java 사용자 스레드에 속하며 다른 스레드는 가상 머신에 의해 자동으로 생성됩니다. 분석 중에는 사용자 스레드에만 관심이 있습니다.
위의 메인 스레드에서 현재 스레드의 호출 컨텍스트를 매우 직관적 인 방식으로 볼 수 있습니다. 한 스레드의 호출의 특정 계층의 의미는 다음과 같습니다.
mytest.fun1 (mytest.java:15) | | | | | | | | | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
또한 스택에는 다음과 같은 명세서가 있습니다 .- 잠금 <0x0000000d77d50b8> (java.lang.object)는 스레드에 이미 잠금 <0x0000000d77d50b8>이 있음을 나타내고 각도 브래킷은 잠금 ID를 나타냅니다. 이것은 시스템에 의해 자동으로 생성됩니다. 우리는 스택이 매번 인쇄된다는 것을 알아야하며, 동일한 ID는 동일한 잠금을 의미합니다. 각 스레드 스택의 첫 번째 줄은 다음과 같이 의미합니다.
"Main"PRIO = 1 TID = 0x000000000238E800 NID = 0XC940 RUNNABLE [0x00000000027AF000] | | | | | | | | | | | | | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Since Java is a 구문 분석 언어와 실행 엔티티는 Java 가상 머신이며, Java 언어의 스레드는 가상 시스템의 로컬 스레드로 실행되며 실제로 Java 스레드 코드를 실행하는 로컬 스레드입니다.
자물쇠의 해석
위의 스레드 스택에서 판단하면 스레드 스택에 포함 된 직접 정보는 다음과 같습니다. 스레드 수, 각 스레드에서 호출 된 메소드 스택 및 현재 잠금 상태. 스레드 수는 직접 계산할 수 있습니다. 스레드가 호출 한 메소드 스택은 하단에서 상단까지 클래스가 현재 스레드에서 호출 한 메소드를 의미합니다. 그리고 잠금 상태는 약간 까다로워 보입니다. 잠금과 관련된 정보는 다음과 같습니다.
스레드가 잠금을 소유하고 있으면 스레드의 스택이 인쇄됩니다.
스레드가 다른 스레드가 잠금을 해제하기를 기다리는 경우 스레드 스택이 인쇄됩니다.
스레드가 잠금을 소유하고 있지만 잠금의 Wait () 메소드를 실행하면 스레드 스택 첫 번째 프린트가 잠겨있는 다음 인쇄물이 켜져 있습니다.
<0x00000000d777d50c8>
스레드 상태의 해석
스레드 스택의 도움으로 많은 유형의 문제를 분석 할 수 있으며 CPU 소비 분석은 스레드 스택 분석의 중요한 부분입니다.
timed_waiting 및 대기 상태의 스레드는 CPU를 소비해서는 안됩니다. Runnable의 스레드의 경우 현재 코드의 특성에 따라 CPU를 소비하는지 판단해야합니다.
순수한 Java 작동 코드 인 경우 CPU를 소비합니다.
네트워크 IO 인 경우 CPU를 거의 소비하지 않습니다.
로컬 코드 인 경우 로컬 코드의 특성에 따라 판단해야합니다 (로컬 스레드 스택은 PSTACK 및 GSTACK을 통해 얻을 수 있음). 순수한 작동 코드 인 경우 CPU를 소비합니다. 일시 중단되면 CPU를 소비하지 않습니다. IO라면 CPU를 많이 소비하지 않습니다.
스레드 스택 문제를 분석하는 방법
스레드 스택은 다음 유형의 질문을 포지셔닝하는 데 매우 유용합니다.
스레드 교착 상태 분석
Java 코드로 인한 과도한 CPU 분석
폭력주기 분석
자원이 충분하지 않은 분석
성능 병목 현상 분석
스레드 교착 상태 분석
교착 상태의 개념에 대해 너무 설명하지 않을 것입니다. 이해하지 못하면 온라인으로 확인할 수 있습니다.
루프의 잠금 의존성으로 인해 둘 이상의 스레드에 의해 형성된 잠금 링은 다음과 같이 진정한 교착 상태를 형성합니다.
하나의 Java 수준을 찾았습니다 교착 상태 : ========================================================== ========================================================================== =========================================================================== =========================================================================== =========================================================================== =========================================================================== =========================================================================== =========================================================================== 잠금 모니터 0x0000000000A9ABC78 (객체 0x00000000000D77363E0, java.lang.object). 위 : ====================================================================================================================== =============================================================================================================== =============================================================================================================== ========================================================================================================================= org.ccgogoing.study.stacktrace.deadlock.testthread2.fun (testthread2.java:35) - 잠금 대기 대기 <0x00000000d77363d0> (java.lang.object) - 잠금 <0x00000000d77363e0> (java.lang.lang.lang.lang.lang.lang.lang.lang.lang. org.ccgogoing.study.stacktrace.deadlock.testthread1 ": at org.ccgogoing.study.stacktrace.deadlock.testthread1.fun (testthread1.java:33) - 잠금 대기 <0x00000000d77363e0> (java.lang). <0x00000000d77363d0> (org.ccgogoing.study.stacktrace.deadlock.testthread1.run (testthread1.java:20)의 <0x000000d77363d0> (java.lang.object).
인쇄 된 스택에서 우리는 "발견 된 Java 수준의 교착 상태"를 볼 수 있습니다. 즉 교착 상태가있는 경우 스택은 교착 상태 분석 결과를 직접 제공합니다.
Java 스레드 세트가 죽으면 게임 오버가 있다는 것을 의미합니다.이 스레드는 항상 거기에 매달려 있으며 계속 실행할 수 없습니다. 교착 상태가있는 스레드가 시스템의 중요한 기능을 실행하는 경우이 교착 상태로 인해 전체 시스템이 마비 될 수 있습니다. 시스템을 복원하려면 시스템을 피하는 일시적이고 유일한 방법은 시스템을 다시 시작하는 것입니다. 그런 다음이 교착 상태를 일으킨 버그를 서두르고 수정하십시오.
참고 : 교착 상태가 된 두 개 이상의 스레드는 CPU를 소비하지 않습니다. 어떤 사람들은 CPU의 100% 사용률이 스레드 교착 상태로 인해 발생한다고 생각합니다. 이 진술은 완전히 잘못되었습니다. Dead Loop 및 루프의 코드는 CPU 집약적이므로 CPU의 100% 사용률을 초래할 수 있습니다. 소켓 또는 데이터베이스와 같은 IO 작업은 CPU를 많이 소비하지 않습니다.