http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E6%96%87/57422.shtml
原文內容:
一個應用開發占用cpu很高,除了確實是計算密集型應用開發之外,通常原因都是出現了死循環。
(友情提示:本博文章歡迎轉載,但請注明出處:hankchen,http://www.blogjava.net/hankchen)
以我們最近出現的一個實際故障為例,介紹怎么定位和解決這類問題。
根據top命令,發現pid為28555的java進程占用cpu高達200%,出現故障。
通過ps aux | grep pid命令,可以進一步確定是tomcat進程出現了問題。但是,怎么定位到具體線程或者代碼呢?
首先顯示線程列表:
ps -mp pid -o thread,tid,time
找到了耗時最高的線程28802,占用cpu時間快兩個小時了!
其次將需要的線程id轉換為16進制格式:
printf "%x\n" tid
最后打印線程的堆棧信息:
js tack pid |grep tid -a 30
找到出現問題的代碼了!
現在來分析下具體的代碼:shortsocketio.readbytes(shortsocketio.java:106)
shortsocketio是應用開發封裝的一個用短連接socket通信的開發工具類。readbytes函數的代碼如下:
public byte[] readbytes(int length) throws ioexception {
if ((this.socket == null) || (!this.socket.isconnected())) {
throw new ioexception("++++ attempting to read from closed socket");
}
byte[] result = null;
bytearrayoutputstream bos = new bytearrayoutputstream();
if (this.recindex >= length) {
bos.write(this.recbuf, 0, length);
byte[] newbuf = new byte[this.recbufsize];
if (this.recindex > length) {
system.arraycopy(this.recbuf, length, newbuf, 0, this.recindex - length);
}
this.recbuf = newbuf;
this.recindex -= length;
} else {
int totalread = length;
if (this.recindex > 0) {
totalread -= this.recindex;
bos.write(this.recbuf, 0, this.recindex);
this.recbuf = new byte[this.recbufsize];
this.recindex = 0;
}
int readcount = 0;
while (totalread > 0) {
if ((readcount = this.in.read(this.recbuf)) > 0) {
if (totalread > readcount) {
bos.write(this.recbuf, 0, readcount);
this.recbuf = new byte[this.recbufsize];
this.recindex = 0;
} else {
bos.write(this.recbuf, 0, totalread);
byte[] newbuf = new byte[this.recbufsize];
system.arraycopy(this.recbuf, totalread, newbuf, 0, readcount - totalread);
this.recbuf = newbuf;
this.recindex = (readcount - totalread);
}
totalread -= readcount;
}
}
}
問題就出在標紅的代碼部分。如果this.in.read()返回的數據小於等於0時,循環就一直進行下去了。而這種情況在網絡擁塞的時候是可能發生的。
至於具體怎么修改就看業務邏輯應該怎么對待這種特殊情況了。
最后,總結下排查cpu故障的開發方法 和技巧有哪些:
1、top命令:linux命令。可以查看實時的cpu使用情況。也可以查看最近一段時間的cpu使用情況。
2、ps命令:linux命令。強大的進程狀態監控命令。可以查看進程以及進程中線程的當前cpu使用情況。屬於當前狀態的采樣數據。
3、js tack:java提供的命令。可以查看某個進程的當前線程棧運行情況。根據這個命令的輸出可以定位某個進程的所有線程的當前運行狀態、運行代碼,以及是否死鎖等等。
4、pstack:linux命令。可以查看某個進程的當前線程棧運行情況。
(友情提示:本博文章歡迎轉載,但請注明出處:hankchen,http://www.blogjava.net/hankchen)
http://binma85.馬開東/blog/778986
開門見山,本文將簡述如何使用java thread dump來分析cpu高使用率以及線程死鎖問題。
一般java thread dump用於web開發中分析web容器或是應用開發服務器開發的性能問題還是比較常用並有效的。常用
此文來自: 馬開東博客 轉載請注明出處 網址:
的入門級web容器tomcat,以及高級別的jboss、websphere、weblogic等的性能調優問題都可以使用java thread dump來分析。首先,闡述一下thread dump常用來解決的是何種問題
(1)高cpu使用
(2)線程死鎖
其次,使用步驟[以jboss為例]
1..get thread dump log
(1)找到應用開發程序所在的進程號,命令如下
- ps aux |grep 'jboss' | grep 'java'
、
獲取需要的pid
(2)執行sudo kill -3 pid獲取thread dump log(pid是第一步獲取)。
注意:在不同的linux環境下執行輸出的日志的地方可能不同。在ibm的powerpc小型機上的linux上執行kill -3 pid會在工作目錄下產生類似javacore.20100409.161739.7614.0001.txt的文件。jboss默認環境下,thread dump log輸出到jboss console,所以thread dump信息會輸出到個人定義的控制台打印log中。
部分示例如下所以:
2010-10-08 20:27:42
full thread dump java hotspot(tm) server vm (16.3-b01 mixed mode):
"http-182.50.0.138-8084-6" daemon prio=10 tid=0x08ce5000 nid=0x6a4c in object.wait() [0x87b5c000]
java.lang.thread.state: waiting (on object monitor)
at java.lang.object.wait(native method)
- waiting on <0x95eb81b0> (a org.apache.tomcat.util.net.jioendpoint$worker)
at java.lang.object.wait(object.java:485)
at org.apache.tomcat.util.net.jioendpoint$worker.await(jioendpoint.java:415)
- locked <0x95eb81b0> (a org.apache.tomcat.util.net.jioendpoint$worker)
at org.apache.tomcat.util.net.jioendpoint$worker.run(jioendpoint.java:441)
at java.lang.thread.run(thread.java:619)
"http-182.50.0.138-8084-5" daemon prio=10 tid=0x08c2e000 nid=0x6a4b in object.wait() [0x87bad000]
java.lang.thread.state: waiting (on object monitor)
at java.lang.object.wait(native method)
- waiting on <0x95ed0600> (a org.apache.tomcat.util.net.jioendpoint$worker)
at java.lang.object.wait(object.java:485)
at org.apache.tomcat.util.net.jioendpoint$worker.await(jioendpoint.java:415)
- locked <0x95ed0600> (a org.apache.tomcat.util.net.jioendpoint$worker)
at org.apache.tomcat.util.net.jioendpoint$worker.run(jioendpoint.java:441)
at java.lang.thread.run(thread.java:619)
"ajp-127.0.0.1-8009-acceptor-0" daemon prio=10 tid=0x894de800 nid=0x6a45 runnable [0x881f3000]
java.lang.thread.state: runnable
at java.net.plainsocketimpl.socketaccept(native method)
at java.net.plainsocketimpl.accept(plainsocketimpl.java:390)
- locked <0x949c1288> (a java.net.sockssocketimpl)
at java.net.serversocket.implaccept(serversocket.java:453)
at java.net.serversocket.accept(serversocket.java:421)
at org.apache.tomcat.util.net.defaultserversocketfactory.acceptsocket(defaultserversocketfactory.java:61)
at org.apache.tomcat.util.net.jioendpoint$acceptor.run(jioendpoint.java:309)
at java.lang.thread.run(thread.java:619)
defaultquartzscheduler_quartzschedulerthread" prio=10 tid=0x8a460800 nid=0x6a38 sleeping[0x88818000]
java.lang.thread.state: timed_waiting (sleeping)
at java.lang.thread.sleep(native method)
at org.quartz.core.quartzschedulerthread.run(quartzschedulerthread.java:394)
(3)獲取線程信息
使用上面的ps或者使用top命令也可以。獲取的線程信息如下所示:
27143 root 20 0 780m 376m 11m s 17 11.5 2:56.48 java
4839 root 20 0 778m 162m 11m s 10 5.0 1717:03 java
5049 root 20 0 764m 147m 11m s 4 4.5 1744:06 java
1 root 20 0 2100 720 624 s 0 0.0 0:28.08 init
2 root 15 -5 0 0 0 s 0 0.0 0:00.00 kthreadd
3 root rt -5 0 0 0 s 0 0.0 0:00.44 migration/0
第一列是十進制pid,需要轉化為16進制后才能和thread dump信息對應。
2.分析thread dump信息[不在列舉示例,只講思想]
(1)分析高cpu使用線程的thread dump信息,查找那些代碼導致高cpu使用。
(2)線程死鎖
a.為了發現線程動態變化,需要多次做thread dump,每次間隔10-30s為佳.
b.線程狀態用 runnable(正在運行)、waiting for monitor(主動等待)、waiting for monitor entry(死鎖)。所以我們最多的是關注runnable和entry類型的線程。
一種典型的死鎖是在server端多個應用開發同時使用同一個jboss資源,這時候需要將多個應用開發分不到不用的隊列中。
http://javag.馬開東/blog/718243
參考文獻:
http://www.51testing.com/?uid-188107-action-viewspace-itemid-226468
1.分析內存的開發工具
eclipse memory analyzer tool(俗稱mat),下載地址為: http://www.eclipse.org/mat/
使用memory analyzer tool(mat)分析內存泄漏(一)
使用memory analyzer tool(mat)分析內存泄漏(二) 使用前需要在linux上通過jmap -dump:format=b,file={$filename} ${pid}方式將heap的內存快照文件給dump出來,然后就可以通過上面的mat進行分析了。注意dump出來的文件名要以bin作為后
此文來自: 馬開東博客 轉載請注明出處 網址:
綴名不然可能識別不了哦.
例如: jmap -dump:format=b,file=a.bin 2298
2.線程狀態分析
"exec-613" id=713 in blocked on lock=com.ss.nio.clientfactory@2262ce5f owned by tomcatthreadpool-exec-553 id=623
"exec-553" id=623 in timed_waiting on lock=com.ss.nio.abstractrequest@35ce75e at java.lang.object.wait(native method)
"nioprocessor-1" id=700 in runnable (running in native) at sun.nio.ch.epollarraywrapper.epollwait(native method) at sun.nio.ch.epollarraywrapper.poll(epollarraywrapper.java:215) at sun.nio.ch.epollselectorimpl.doselect(epollselectorimpl.java:65) at sun.nio.ch.selectorimpl.lockanddoselect(selectorimpl.java:69) at sun.nio.ch.selectorimpl.select(selectorimpl.java:80)
"rmi tcp connection(8)-172.25.3.81" id=698 in runnable at sun.management.threadimpl.getthreadinfo0(native method) at sun.management.threadimpl.getthreadinfo(threadimpl.java:145) at sun.reflect.nativemethodaccessorimpl.invoke0(native method) at sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:39) at sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:25) at java.lang.reflect.method.invoke(method.java:597)
runnable(正在運行的,消耗cpu) timed_waiting(等待被分配到cpu運行的,現在不消耗cpu) blocked(被阻塞,在阻塞解除前不能被分配cpu執行,現在不消耗cpu)
exec-613占用了鎖,exec-553需要的鎖被exec-613占用無法執行處於blocked狀態.
nioprocessor-1正在運行,並且他的開發方法 在調用native開發方法 .
rmi tcp connection(8)-172.25.3.81正在運行.
(一) jinfo jinfo打印一個給定的java進程或核心文件或一個遠程調試服務器開發的java配置信息。配置信息包括java系統開發屬性和jvm命令行標志(更多信息,請參考《jinfo-configuration info》)。 (二) jmap jmap:如果這個開發工具不使用任何選項(除了pid或core選項)運行,那么它顯示類似於solaris的pmap開發工具所輸出的信息。這個開發工具支持針對java堆可觀察性的若干其它選項。 在java se 6平台中,新加入了一個-dump選項。這樣可以使jmap能夠把java堆信息復制到一個文件中,然后我們可以使用新的jhat命令(見下面一節)來分析它。 jmap -dump選項並不使用solaris libproc來實現實時處理;而是,它運行當前正運行的jvm中的一小段代碼,由此來實現堆復制。既然這種堆復制代碼運行於jvm內部,那么其速度是比較快的。堆復制的效果大致相當於實現一次"完全的gc"(對整個堆的垃圾收集),再加上把該堆的內容寫入到文件中。實現堆復制的另外一種可能的思路是使用 gcore來進行核心復制並且運行"jmap -dump"(這與以"離線"方式運行的核心復制形成對照)。
可以輸出某個java進程內存內對象的情況,甚至可以將vm 中的heap,以二進制輸出成文本。