CPU使用率高
- 找出使用率高的進程的pid
top
- 找出使用率高的線程tpid
top -p pid -H
- 查看使用率高的線程當(dāng)前在干什么
jstack -l pid > stack.log
// 將線程的tpid轉(zhuǎn)為16進制绝骚,到stack.log中查找
grep tpid stack.log -a3
GC問題
// -t:打印時間戳恩静,1s每隔1秒打印一次
jstat -gcutil -t pid 1s
也可以通過查看gc日志來觀察問題
內(nèi)存泄漏
- 執(zhí)行FullGC后不能回收的內(nèi)存不斷增加
- 執(zhí)行jstat -gcutil pid,查看Old區(qū)的使用情況阵漏,如果接近100%罢低,則代表內(nèi)存不足》急可以先增大內(nèi)存枪芒,如果還是不斷增長到溢出,則考慮是否有內(nèi)存泄漏問題
- 執(zhí)行jmap -histo:live pid > memory.log挺庞,統(tǒng)計所有存活對象的個數(shù)晰赞,觀察那些數(shù)量最多的對象,特別是自己寫的對象和存放到集合里沒有釋放的對象
- 如果還是無法定位选侨,則執(zhí)行jmap -dump導(dǎo)出整個Heap掖鱼,然后使用工具進行分析,注意看自己寫的類的依賴關(guān)系援制,看看是不是使用完沒有釋放锨用,或者一次性查詢過多的數(shù)據(jù)導(dǎo)致內(nèi)存溢出
線程分析
如果CPU使用率不高,但程序性能低下隘谣,則可考慮對線程進行分析增拥,看看各個主要的線程都在做什么啄巧,是否有鎖爭用或IO阻塞問題
為了方便分析,最好給每個線程或者線程池命名
jstack -l pid > stack.log
線程狀態(tài)
狀態(tài) | 描述 |
---|---|
New | 線程剛被創(chuàng)建掌栅,還沒有被執(zhí)行 |
Runnable | 線程正在執(zhí)行 |
Blocked | 等待其他線程釋放鎖 |
Waiting | 調(diào)用了wait或join方法秩仆,無限等待 |
Timed_Waiting | 調(diào)用了sleep、wait(interval)猾封、join(interval)方法澄耍,有限等待 |
觀察
死鎖
如果JVM發(fā)現(xiàn)有死鎖存在,會在日志中出現(xiàn)Found one Java-level deadlock
Waiting on condition
在等待一個條件的發(fā)生晌缘,來把自己喚醒齐莲,或者調(diào)用了sleep方法
此時線程狀態(tài):
WAITING(parking):一直等待那個條件發(fā)生
TIMED_WAITING(parking或sleeping):定時等待,即使條件不發(fā)生磷箕,時間到了也可以自己喚醒
如果發(fā)現(xiàn)大量線程處于此狀態(tài)选酗,并且從線程的堆棧上查看到是正在執(zhí)行網(wǎng)絡(luò)讀寫,這可能是一個網(wǎng)絡(luò)瓶頸問題或者第三方響應(yīng)慢的問題
Blocked
線程所需要的資源長時間等待卻一直無法獲取岳枷,被標(biāo)識為阻塞狀態(tài)芒填,可以理解為等待資源超時的線程。線程堆棧中一般存在Waiting to Lock
Waiting for monitor entry 和 in Object.wait()
每個 Monitor在某個時刻空繁,只能被一個線程擁有殿衰,該線程就是Active Thread,而其它線程都是Waiting Thread盛泡,分別在兩個隊列 Entry Set和Wait Set里面等候闷祥。 在Entry Set中等待的線程狀態(tài)是Waiting for monitor entry,而在Wait Set中等待的線程狀態(tài)是in Object.wait()傲诵。當(dāng)被調(diào)用notify或notifyAll時凯砍,只有在Wait Set中的線程會被喚醒
Waiting for monitor entry:等待進入一個臨界區(qū) ,所以它在Entry Set隊列中等待掰吕。此時線程狀態(tài)一般都是Blocked,如果存在大量線程在此狀態(tài)颅痊,可能是一個全局鎖阻塞住了大量線程殖熟。隨著時間流逝,waiting for monitor entry的線程越來越多斑响,沒有減少的趨勢菱属,可能意味著某些線程在臨界區(qū)里呆的時間太長了,以至于越來越多新線程遲遲無法進入臨界區(qū)
當(dāng)線程獲得了Monitor舰罚,如果發(fā)現(xiàn)線程繼續(xù)運行的條件沒有滿足纽门,它則調(diào)用對象(一般就是被 synchronized 的對象)的 wait() 方法,放棄了Monitor营罢,進入Wait Set隊列赏陵。此時線程狀態(tài)大致為以下幾種:TIMED_WAITING (on object monitor)和 WAITING (on object monitor)
等待IO
有時候線程狀態(tài)是Runnable饼齿,但卻是在等待IO
"socketReadThread" prio=6 tid=0x0000000006a0d800 nid=0x1b40 runnable
[0x00000000089ef000] java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
總結(jié)
- 如果cpu使用率不高,但性能低下蝙搔,一般都是由鎖或IO阻塞造成缕溉,這時要注意查看狀態(tài)為BLOCKED或者Waiting的線程,看它們需要等待什么鎖或者是否出現(xiàn)了死鎖吃型,再考慮如何優(yōu)化并發(fā)
- 如果發(fā)現(xiàn)有大量的線程都在處在 Wait on condition证鸥,從線程 stack看,正等待網(wǎng)絡(luò)讀寫勤晚,這可能是一個網(wǎng)絡(luò)瓶頸的征兆枉层。因為網(wǎng)絡(luò)阻塞導(dǎo)致線程無法執(zhí)行。一種情況是網(wǎng)絡(luò)非常忙赐写,幾乎消耗了所有的帶寬鸟蜡,仍然有大量數(shù)據(jù)等待網(wǎng)絡(luò)讀寫;另一種情況也可能是網(wǎng)絡(luò)空閑血淌,但由于路由等問題矩欠,導(dǎo)致包無法正常的到達