블로그 이미지
잡초 개발자의 좌충우돌 이야기 yysvip

카테고리

분류 전체보기 (211)
Java Programing (24)
Web Programing (20)
Database (36)
Tool (46)
까칠한 IT (27)
까칠한 정보 (42)
까칠한 Strory (16)
까칠한 Project (0)
Total905,954
Today3,437
Yesterday7,350

달력

« » 2018.10
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      

공지사항

Thread dump 란

  까칠한 남자 Strory       yysvip.tistory.com       잡초 개발자 까찰한 쑤의 좌충우돌 개발 이야기

 

 

Java에서 Thread dump란 무엇일까요 ?

 

말그대로 Java 즉 JVM에서 사용하는 Thread들에 대한 dump입니다.
다르게 말하면 Thread call stack 정보라고 보시면 이해가 쉬울것 같습니다.


예전에는 Java가 Thread를 사용하는 방식이 크게 두가지였습니다.

 

하나는 Native Thread를 사용하는 방식 (요즘에 모두가 이 방식을 사용하고 있지요)
또 하나는 Green Thread 방식이라고 해서 JVM이 자체로 Thread 기능을 제공하는 것입니다.

 

지금은 이러한 Green Thread 방식은 없어졌습니다.


이 Green Thread에 대한 저의 경험을 말씀드리면 Linux 기반의 JVM이였는데 Thread 갯수를 지정하고 구동하면 그 Thread가 모두 프로세스로 기동되는 것입니다. 그래서 프로세스 정보를 보면 java 프로세스가 줄줄이 기동되어 있는것을 볼 수 있습니다. 그래서 Resource 문제로 Thread를 많이 사용할 수도 없습니다.

 

Native Thread 방식이 보편화 되면서 이제는 OS에서 제공하는 Thread의 방식을 모두 사용하게 되었습니다.
각 OS 별로 Thread 에 대한 내부 구조(스케쥴링, 라이프사이클등)가 조금씩 틀릴 수 있으니 이러한 부분들이 JVM에 영향을 미칠 수 밖에 없습니다.

 

모든 OS는 Process와 Thread의 관계는 1:1 또는 1:N으로 구성됩니다.


Thread 기반으로 구현하지 않아도 기본적으로 Single Thread로 구동하도록 되어 있습니다.

이렇게 멀티 Thread 기반으로 구조가 모두 바뀌었지만 실제 Unix 기반의 Native 프로그램(C 기반 프로그램)을 멀티 Thread 기반으로 구현하는 경우는 별로 없습니다.


그 만큼 구현의 방식도 어렵거니와 C 프로그램을 작성하는 개발자들이 Thread 기반의 구현을 꺼려하는 경향이 있습니다.

Java는 이러한 어려운점을 쉽게 구현할 수 있도록하여 Thread의 사용을 보편화 시켰습니다.
(Thread 객체 생성 --> run() 수행 끝....)

 

물론 Thread 프로그램밍을 위해서는 동기화라는 어려운 산을 넘어야 하지만 개발을 위한 초기 진입은 쉽게 할 수 있게 되었습니다.


Java는 멀티 Thread 들이 현재 무엇을 하고 있는지에 대한 Call stack을 손쉽게 확인할 수 있는 기능을 제공하는데 이것이 바로 Thread dump입니다.

 

물론 Unix기반의 프로세스에 대한 Call stack을 확인하기 위해 pstack(Linux, Sun, HP-UX), procstack(AIX) 등을 사용할 수 있습니다.


이러한 툴들은 Native stack(C stack)에 대해서만 확인 할 수 있습니다.

 

Java는 JVM 위에 bytecode를 구동하는 것이기 때문에 이러한 Native stack으로는 Java stack을 확인할 수가 없습니다.
JVM에 대해 pstack 명령어등을 사용하면 Native stack (C++로 구현된 JVM stack)을 확인하실 수 있습니다.

 

다음은 JVM에 대한 Native stack(HP-UX Java 1.5) 입니다.

 

--------------------------------  lwpid : 9007580   -------------------------------

 0: 60000000c038a4b0 : __ksleep() + 0x30 (⁄usr⁄lib⁄hpux32⁄libc.so.1)
 1: 60000000c014c130 : __mxn_sleep() + 0xaa0 (⁄usr⁄lib⁄hpux32⁄libpthread.so.1)
 2: 60000000c00e3f10 : pthread_cond_wait() + 0xdd0 (⁄usr⁄lib⁄hpux32⁄libpthread.so.1)
 3: 60000000c00e6370 : pthread_cond_timedwait() + 0xf0 (⁄usr⁄lib⁄hpux32⁄libpthread.so.1)
 4: 60000000cc2243c0 : os::Hpux::Event::down(long long)() at ⁄CLO⁄Components⁄JAVA_HOTSPOT⁄Src⁄src⁄os⁄hp-ux⁄vm⁄os_hp-ux.hpp:412 
 5: 60000000cc2232c0 : ObjectMonitor::wait(long long,bool,Thread*)() at ⁄CLO⁄Components⁄JAVA_HOTSPOT⁄Src⁄src⁄os⁄hp-ux⁄vm⁄objectMonitor_hp-ux.cpp:916 
 6: 60000000cc3d6b90 : ObjectSynchronizer::wait(Handle,long long,Thread*)() at ⁄CLO⁄Components⁄JAVA_HOTSPOT⁄Src⁄src⁄share⁄vm⁄runtime⁄synchronizer.cpp:475 
 7: 60000000cc0632a0 : JVM_MonitorWait() at ⁄CLO⁄Components⁄JAVA_HOTSPOT⁄Src⁄src⁄share⁄vm⁄prims⁄jvm.cpp:450 
 8: 2000000063400940 : (unknown) () (unknown)

물론 JVM 벤더별로 Native stack과 Java stack을 같이 보여주기 위한 기능

(HP JVM unwind library등 http://blog.naver.com/bumsukoh/110112408842 참고)을 제공하기도 합니다.

 

Thread dump는 순수하게 현재 Thread가 어느 Java stack을 수행하는지를 보기 위한 것입니다.
굉장히 손쉽게 dump 정보를 얻을 수가 있는데요.

 

가장 일반적으로는 "kill -3 (SIGQUIT)" signal을 통해 가능합니다.
JVM 내부적으로 SIGQUIT를 catch하여 각 Thread에 대한 stack을 출력하게 됩니다.

 
다음은 JVM에 대한 Java stack(Sun Java 6.0) 입니다.

"TP-Processor19" daemon prio=6 tid=0x545fc400 nid=0x2e28 waiting for monitor entry [0x593de000..0x593dfb98]
   java.lang.Thread.State: BLOCKED (on object monitor)
 at com.ibatis.common.jdbc.SimpleDataSource.pushConnection(SimpleDataSource.java:522)
 - waiting to lock <0x13c89418> (a java.lang.Object)
 at com.ibatis.common.jdbc.SimpleDataSource.access$100(SimpleDataSource.java:52)
 at com.ibatis.common.jdbc.SimpleDataSource$SimplePooledConnection.invoke(SimpleDataSource.java:954)
 at $Proxy68.close(Unknown Source)
 at com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransaction.close(JdbcTransaction.java:81)
 at com.ibatis.sqlmap.engine.transaction.TransactionManager.end(TransactionManager.java:110)
 at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.endTransaction(SqlMapExecutorDelegate.java:780)
 at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.endTransaction(SqlMapSessionImpl.java:170)
 at com.ibatis.sqlmap.engine.impl.SqlMapClientImpl.endTransaction(SqlMapClientImpl.java:149)
 at com.ibatis.dao.engine.transaction.sqlmap.SqlMapDaoTransaction.commit(SqlMapDaoTransaction.java:41)
 at com.ibatis.dao.engine.transaction.sqlmap.SqlMapDaoTransactionManager.commitTransaction(SqlMapDaoTransactionManager.java:75)
 at com.ibatis.dao.engine.impl.DaoContext.commitTransaction(DaoContext.java:101)
 at com.ibatis.dao.engine.impl.StandardDaoManager.commitTransaction(StandardDaoManager.java:88)
 at net.xxxxx.util.comm.MyDaoManager.commitTransaction(MyDaoManager.java:78)
 at net.xxxxx.std.bean.ObjectBean.getImage(ObjectBean.java:371)
 at net.xxxxx.std.action.ObjectAction.getImage(ObjectAction.java:39)
 at sun.reflect.GeneratedMethodAccessor434.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.apache.struts.actions.DispatchAction.dispatchMethod(DispatchAction.java:269)
 at org.apache.struts.actions.DispatchAction.execute(DispatchAction.java:170)
 at org.apache.struts.chain.commands.servlet.ExecuteAction.execute(ExecuteAction.java:58)
 at org.apache.struts.chain.commands.AbstractExecuteAction.execute(AbstractExecuteAction.java:67)
 at org.apache.struts.chain.commands.ActionCommandBase.execute(ActionCommandBase.java:51)
 at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:190)
 at org.apache.commons.chain.generic.LookupCommand.execute(LookupCommand.java:304)
 at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:190)
 at org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:283)
 at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
 at org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:449)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
 at net.xxxxx.util.filter.SmartFilter.doFilter(SmartFilter.java:96)
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
 at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
 at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
 at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
 at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
 at org.apache.catalina.ha.session.JvmRouteBinderValve.invoke(JvmRouteBinderValve.java:227)
 at org.apache.catalina.ha.tcp.ReplicationValve.invoke(ReplicationValve.java:347)
 at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
 at org.apache.jk.server.JkCoyoteHandler.invoke(JkCoyoteHandler.java:190)
 at org.apache.jk.common.HandlerRequest.invoke(HandlerRequest.java:291)
 at org.apache.jk.common.ChannelSocket.invoke(ChannelSocket.java:774)
 at org.apache.jk.common.ChannelSocket.processConnection(ChannelSocket.java:703)
 at org.apache.jk.common.ChannelSocket$SocketConnection.runIt(ChannelSocket.java:896)
 at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:690)
 at java.lang.Thread.run(Thread.java:619)

이러한 Thread dump는 Java 벤더별(Sun 계열, IBM 계열..)로 구조가 조금씩 다릅니다.(뒤에 설명드리도록 하겠습니다.)

 

 

자! 그럼 이런 Thread dump를 통해 분석이 필요한 경우는 언제일까요 ?

 

- Java 어플리케이션이 Hang 상태가 될 때

 

 Thread dump를 이용하여 트러블슈팅하는 가장 흔한 상황이 Hang  상태입니다.
 Hang 상태는 Java 어플리케이션의 Thread들이 멈춰있는 상황이므로 Thread dump를 통해 Thread들이 현재 수행하고 있는 상태를 확인하는 것으로 원인을 파악할 수 있습니다.
Hang 상태가 발생하는 경우는 다양하며 주요한 원인으로는 Object locking, Heap locking등을 들 수 있습니다.

 

 

- Java 어플리케이션의 처리가 늦을 경우

 

Java 어플리케이션의 처리가 늦을 경우 어느 stack에서 지연되는지 확인하기 위해 활용할 수 있습니다.
 Hang은 아니나 Thread들이 sleep 단계에서 모두 대기하는 상태가 있을 수 있으며, 이러한 경우의 대표적인 경우가 SQL문 수행의 지연으로 DB에서의 데이터를 Socket read하기 위해 대기하는 상태라 할 수 있습니다.

 

 

- Java 어플리케이션의 CPU 사용율이 높을 경우(특정 Thread가 높을 경우)

 

 Java 어플리케이션의 특정 Thread의 CPU 사용율이 높을 경우 대상 Thread의 수행 상태와 stack을 확인하기 위해 Thread dump를 활용 할 수 있습니다.
이러한 상황의 트러블슈팅을 위해서는 우선적으로 Java 어플리케이션의 각  Thread의 CPU 사용율을 확인하는 것이 선행되어야 하며, 각 OS별로 제공하는 명령어를 통해 확인 할 수 있습니다.

(HP-UX의 glance, IBM의 "ps -mp", Sun의 "prstat" 명령어 활용)

 

이렇게 Thread dump가 무엇이며, 어느 상황에서 활용할 수 있는지에 대해 알아 보았습니다.

 

[출처] 1. Thread dump 분석 - Thread dump 란 ?|작성자 범서기

 

Posted by yysvip

최근에 달린 댓글

최근에 받은 트랙백

글 보관함