Thread dump
Thread란
프로세스 내에서 실행되는 흐름의 단위
※ 프로세스 : 특정 서비스를 위해, OS내에서 메모리를 할당받아 수행되는 프로그램
하나의 프로세스에 여러개의 쓰레드가 존재할 수 있으며, 각 쓰레드는 아래의 상태값을 가진다.
① NEW : 스레드가 생성되었지만 스레드가 아직 실행할 준비가 되지 않았음
② RUNNABLE : 스레드가 실행되고 있거나 실행준비되어 스케쥴링은 기달리는 상태
③ WAITING : 다른 스레드가 notify(), notifyAll()을 불러주기 기다리고 있는 상태(동기화)
④ TIMED_WAITING : 스레드가 주어진 시간동안, 대기하고 있는 상태
⑤ BLOCK : 사용하고자하는 객체의 Lock이 풀릴때까지 대기하는 상태
⑥ TERMINATED : 스레드가 종료한 상태
JVM에 의해 관리되며, 설명해놓은 그림이 있어 가져와봤다.
Thread dump
프로세스에 수행중인 모든 쓰레드들의 상태를 스냅샷 수행
보통 아래 상황일때 Thread 분석을 한다.
1) 어플리케이션이 Hang 상태일 때
2) 서비스 처리가 늦어질 때
3) CPU 사용량이 높을 때
Thread Lock이 원인일수도, 여유 Thread가 없어서 일수도 있다.
SQL 지연일수도, 특정 코드 Stack 에서 막힌것일수도 있어 분석툴로 확인해야한다.
하지만 하나의 스냅샷만으로는 정확한 원인파악이 어렵다.
여러개의 스냅샷을 비교하며, 쓰레드의 변화상태를 통해야만 특정 쓰레드를 원인지을수 있다.
빠른시간내에 여러개 덤프를 떠서, 순간의 변화사항 분석이 필요하다
( Ex: 5초내 여러개 Thread dump 획득 )
생성방법
1) kill -3 $PID
2) jstack 활용
jps-v 로 PID를 확인 가능하며, ps -ef | grep 을 통해서도 특정 PID를 알 수 있다.
jstack $PID > log.txt
분석
일반적으로 쓰레드 덤프 구조는 아래와 같다
"pool-1-thread-13" prio=6 tid=0x000000000729a000 nid=0x2fb4 runnable [0x0000000007f0f000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.read(SocketInputStream.java:129)at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)- locked <0x0000000780b7e688> (a java.io.InputStreamReader)at java.io.InputStreamReader.read(InputStreamReader.java:167)at java.io.BufferedReader.fill(BufferedReader.java:136)at java.io.BufferedReader.readLine(BufferedReader.java:299)- locked <0x0000000780b7e688> (a java.io.InputStreamReader)at java.io.BufferedReader.readLine(BufferedReader.java:362)
쓰레드 이름: Thread-(Number) 혹은 pool-(number)-thread-(number)로 지정된 고유이름
우선순위 : 쓰레드의 우선순위
쓰레드 ID
쓰레드 상태
쓰레드 콜스택
저런 로그들이 쓰레드마다 존재하기에, 육안으로 구별하기는 힘들다.
보통 분석툴을 사용하여 변화사항을 비교한다.
분석툴
1) Ibm thread and monitor dump analyzer for java
2) fastThread ( https://fastthread.io/ )
분석예시
※ 해당부분은 타 블로그에서 분석한 내용을 가져와 서술하였습니다. - CPU 사례
( https://d2.naver.com/helloworld/10963 )
먼저, CPU를 가장 많이 점유하는 스레드가 무엇인지 추출한다. 추출한 결과에서 CPU를 가장 많이 사용하는 LWP(light weight process)와 고유 번호를 확인한다. [user@linux ~]$ ps -mo pid,lwp,stime,time,cpu -C java PID LWP STIME TIME %CPU 10029 - Dec07 00:02:02 99.5 - 10039 Dec07 00:00:00 0.1 - 10040 Dec07 00:00:00 95.5 파악한 PID를 통해 thread dump를 획득한다. "NioProcessor-2" prio=10 tid=0x0a8d2800 nid=0x2737 runnable [0x49aa5000] java.lang.Thread.State: RUNNABLE at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method) at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:210) at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:65) at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:69) - locked <0x74c52678> (a sun.nio.ch.Util$1) - locked <0x74c52668> (a java.util.Collections$UnmodifiableSet) - locked <0x74c501b0> (a sun.nio.ch.EPollSelectorImpl) 수초내 지속적으로 dump를 획득하여, 해당 Stack 별 변화를 비교해보며 원인을 찾는다. |