Thread Dump 和Java應用診斷

Thread Dump是非常有用的診斷Java應用問題的工具,每一個Java虛擬機都有及時生成顯示所有線程在某一點狀態的thread-dump的能力。雖然各個Java虛擬機thread dump打印輸出格式上略微有一些不同,但是Thread dumps出來的信息包含線程;線程的運行狀態、標識和調用的堆棧;調用的堆棧包含完整的類名,所執行的方法,如果可能的話還有源代碼的行數。

Thread Dump特點:

  • 能在各種操作系統下使用
  • 能在各種Java應用服務器下使用
  • 可以在生產環境下使用而不影響系統的性能
  • 可以將問題直接定位到應用程序的代碼行上

Thread Dump能診斷的問題包括:

  • 查找內存泄露,常見的是程序裏load大量的數據到緩存
  • 發現死鎖線程

Sun的JVM用下列方法可以產生Thread Dump堆棧信息:

1,Solaris OS
<ctrl>-’/’ (Control-Backslash)
 kill -QUIT <pid>

2, HP-UX/UNIX/Linux
Kill -3 PID 
PID通過下面方法獲取
ps -efHl | grep 'java' **. **

3,Windows
直接對MSDOS窗口的程序按Ctrl-break

有些Java應用服務器是在控制檯上運行,如Weblogic,爲了方便獲取threaddump信息,在weblogic啓動的時候,最好將其標準輸出重定向到一個文件,用"nohup sh startWebLogic.sh > start.log &"命令,執行"kill -3 <pid>",Stack trace就會輸出到start.log裏。Tomcat的Thread Dump會輸出到命令行控制檯或者logs的catalina.out文件裏。爲了反映線程狀態的動態變化,需要接連多次做thread dump,每次間隔10-20s。

IBM JVM下產生Thread Dump:

在AIX上用IBM的JVM,內存溢出時默認地會產生javacore文件(關於cpu的)和heapdump文件(關於內存的)。如果沒有參照下列方法:
1 choose one cluster member, set the following before this server start:
在was啓動前設置下面環境變量(可以加在啓動腳本中)
export IBM_HEAPDUMP=true
export IBM_HEAP_DUMP=true
export IBM_HEAPDUMP_OUTOFMEMORY=true
export IBM_HEAPDUMPDIR=<directory path>

2 please use set command to make sure you do not have DISABLE_JAVADUMP parameter
then start this cluster member.
用set命令檢查參數設置,確保沒有設置DISABLE_JAVADUMP,然後啓動server

3 when you find free memory < 50% when no heavy access, please run kill -3 <pid>
執行kill -3 <pid>命令可以生成javacore文件和heapdump文件(pid爲was java進程的id號,可以用ps -ef|grep java 查到),可以多執行幾次,按照下面操作進行

ps -ef > psef1.txt
ps aux > psaux1.txt
vmstat 5 10 > vmstat.txt
kill -3 <app server id>
wait for 2 mins
kill -3 <app server id>
wait for 2 mins
kill -3 <app server id>
netstat -an> netstat2.txt
ps -ef > psef2.txt
ps aux > psaux2.txt
將上面產生的 txt 文件和/usr/WebSphere/AppServer/javacore*文件和heapdump文件拷貝到本地,然後刪除這些文件,因爲這些文件會佔用較大的文件系統空間。
將/usr/WebSphere/AppServer/logs/wlmserver1(或2)目錄下當天產生的日誌拷貝出來


在IBM JVM產生的javacore或者Threaddump文件中應用服務器Web容器的常見線程狀態

Idle線程:一個已經準備好接受請求的線程,但是沒有和插件或者客戶端建立連接
Keep-Alive線程:是一個已經準備好接受請求的線程,並且已經和插件或者客戶端建立連接
正在接受請求的線程:是一個線程正在讀取request的內容或者頭部

下面就給出各種線程在javacore或者Threaddump中的表現形式:

Idle線程:
"Servlet.Engine.Transports : 20" (TID:0x427F190, sys_thread_t:0x15D175E8, state:R, native ID:0xBB8) prio=5
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:429)
at com.ibm.ws.util.BoundedBuffer.take(BoundedBuffer.java:161)
at com.ibm.ws.util.ThreadPool.getTask(ThreadPool.java(Compiled Code)) at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java(Compiled Code))

Keep-alive線程 (非SSL模式):
"Servlet.Engine.Transports : 20" (TID:0x427F190, sys_thread_t:0x15D175E8, state:R, native ID:0xBB8) prio=5
at java.net.SocketInputStream.socketRead(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:86)
at com.ibm.ws.io.Stream.read(Stream.java)
at com.ibm.ws.io.ReadStream.readBuffer(ReadStream.java)
at com.ibm.ws.io.ReadStream.read(ReadStream.java)
at com.ibm.ws.http.HttpRequest.readRequestLine(HttpRequest.java)
at com.ibm.ws.http.HttpRequest.readRequest(HttpRequest.java)
at com.ibm.ws.http.HttpConnection.readAndHandleRequest(HttpConnection.java)
at com.ibm.ws.http.HttpConnection.run(HttpConnection.java)
at com.ibm.ws.util.CachedThread.run(ThreadPool.java)

Keep-alive線程 (SSL模式):
"Servlet.Engine.Transports : 12" (TID:0x458DBA18, sys_thread_t:0x60B297C0, state:R, native ID:0x427E) prio=5
at java.net.SocketInputStream.socketRead(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java(Compiled Code))
at com.ibm.sslite.s.a(Unknown Source)(Compiled Code)
at com.ibm.sslite.s.b(Unknown Source)(Compiled Code)
at com.ibm.sslite.s.a(Unknown Source)(Compiled Code)
at com.ibm.sslite.a.read(Unknown Source)(Compiled Code)
at com.ibm.jsse.a.read(Unknown Source)(Compiled Code)
at com.ibm.ws.io.Stream.read(Stream.java(Compiled Code))
at com.ibm.ws.io.ReadStream.readBuffer(ReadStream.java(Inlined Compiled Code))
at com.ibm.ws.io.ReadStream.read(ReadStream.java(Inlined Compiled Code))
at com.ibm.ws.http.HttpRequest.readRequestLine(HttpRequest.java(Compiled Code))
at com.ibm.ws.http.HttpRequest.readRequest(HttpRequest.java(Compiled Code))
at com.ibm.ws.http.HttpConnection.readAndHandleRequest(HttpConnection)
at com.ibm.ws.http.HttpConnection.run(HttpConnection.java(Compiled Code))
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:672)


正在接受請求的線程:
at java.net.SocketInputStream.socketRead(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:85)
at com.ibm.ws.io.Stream.read(Stream.java:17)
at com.ibm.ws.io.ReadStream.readBuffer(ReadStream.java:411)
at com.ibm.ws.io.ReadStream.read(ReadStream.java:110)
at com.ibm.ws.http.HttpConnection.run(HttpConnection.java:448)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:672)


Sun JVM的常見線程狀態

對於thread dump信息,主要關注的是線程的狀態和其執行堆棧
線程的狀態一般爲三類
Runnable(R):當前可以運行的線程
Waiting on monitor(CW):線程主動wait
Waiting for monitor entry(MW):線程等鎖
一般關注的都是第一和第三種狀態的線程
Cpu很忙則關注runnable的線程
Cpu閒則關注waiting for monitor entry的線程
一種典型的死鎖是由於在server端應用(比如servlet)中請求由同一weblogic實例server的資源
解決辦法就是將該servlet放到另外的執行隊列裏去執行

下面給出一個典型的死鎖線程(注意STUCK關鍵字):

"[STUCK] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'" daemon prio=10 tid=02fe9a18 nid=35 lwp_id=7518924 runnable [440dd000..440db878]
 at java.net.SocketInputStream.socketRead0(Native Method)
 at java.net.SocketInputStream.read(SocketInputStream.java:134)
 at weblogic.jdbc.oracle.net8.OracleDataProvider.getArrayOfBytesFromSocket(Unknown Source)
 at weblogic.jdbc.oracle.net8.OracleDataProvider.readFirstPacketInBuffer(Unknown Source)
 at weblogic.jdbc.oracle.net8.OracleDataProvider.readPacket(Unknown Source)
 at weblogic.jdbc.oracle.net8.OracleDataProvider.receive(Unknown Source)
 at weblogic.jdbc.oracle.net8.OracleNet8NSPTDAPacket.sendRequest(Unknown Source)
 at weblogic.jdbc.oracle.OracleImplStatement.fetchNext(Unknown Source)
 at weblogic.jdbc.oracle.OracleImplStatement.fetchNext2(Unknown Source)
 at weblogic.jdbc.oracle.OracleImplResultset.fetchAtPosition(Unknown Source)
 at weblogic.jdbc.base.BaseImplResultSet.next(Unknown Source)
 at weblogic.jdbc.base.BaseResultSet.next(Unknown Source)
 - locked <55f25550> (a weblogic.jdbc.oracle.OracleConnection)
 at weblogic.jdbc.wrapper.ResultSet_weblogic_jdbc_base_BaseResultSet.next(Unknown Source)
 at org.hibernate.loader.Loader.doQuery(Loader.java:685)

UNIX/Linux下可用top、vmstat或prstat命令觀察系統資源狀況

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章