1. 程式人生 > >監控java執行緒

監控java執行緒

    在上一篇文章《java多執行緒和多核心測試》中,我們大致能分析出執行緒應該佔有多少cpu資源,但是,程式可能並沒有按我們預想的去執行,這樣,我們有時候需要能夠有效的監控它。好在jdk的devel包已經帶了很多的監控工具來完成這個任務。

    1)獲取執行程式的程序id, 可以通過jps或者ps 過濾出來。

    2)通過top -p pid來檢視該程序總的資源使用情況

    3)通過jstack -l pid, 能實時打印出某個時刻所有執行緒的執行堆疊,包括每個執行緒的名字和id,id和名字的對應關係是不會變的。

    4)  通過top -H -p pid 來實時看到每個執行緒的cpu使用情況,如果某個執行緒異常的佔有很多cpu, 我們很快就可以識別出來。

     根據執行緒Id定位執行緒

     有了上面的工具加上程式的日誌,完全可以定位到每個執行緒的資源使用情況。但是還是會遇到一些問題:

     1,如何根據執行緒的名字找到執行緒

     這個其實很簡單,在top -H -p裡看到某個執行緒一直佔用很多cpu,如id為7593,轉成16進製為0x1da9, 那麼可以在jstack -l 裡通過這個id 找到這個執行緒在幹什麼,如jstack -l輸出:

     "SSL Stomp Reactor" daemon prio=10 tid=0x00007fa3e40ac800 nid=0x1da9 runnable [0x00007fa4b2bed000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
        at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
        at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:79)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
        - locked <0x0000000608e5db80> (a sun.nio.ch.Util$2)
        - locked <0x0000000608e5db90> (a java.util.Collections$UnmodifiableSet)
        - locked <0x0000000608e5db38> (a sun.nio.ch.EPollSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
        at org.ovirt.vdsm.jsonrpc.client.reactors.Reactor.select(Reactor.java:47)
        at org.ovirt.vdsm.jsonrpc.client.reactors.Reactor.run(Reactor.java:59)

   Locked ownable synchronizers:
        - None
      我們不僅知道了這個執行緒在幹什麼,而且還知道了該執行緒的名字,而java的日誌輸出裡一般都有執行緒的名字,所以,我們可以通過日誌進一步知道執行緒在做什麼,然後再分析是哪塊邏輯出了問題。

    2,使用了執行緒池,執行緒執行的任務是不停的變換的,導致用top -H -P 看的時候,執行緒的id一直在不停的變

    跟上面類似,我們只能抓取某個時刻使用cpu較多的執行緒id, 但是我們再用jstack去掃描執行緒的時候,可能這時候這個執行緒已經被排程去幹別的任務了,所以得再想想變通的辦法。

    我們知道執行緒池裡的執行緒可以排程幹不同的任務,但是其id和名字的對應關係還是不會變的,我們抓取到瞬時某個cpu的異常情況後,可以找到這個執行緒的名字,然後再去日誌裡面檢視這個執行緒的所有日誌,就知道這個執行緒最近都幹了些啥,很可能我們抓取了不同時刻3個執行緒出現異常,而這三個執行緒乾的其實是同樣的一個任務,這樣我們就能鎖定目標了。

   案例分享:

   1)在監控中,我們發現有兩套相同的環境,負載也差不多,卻發現一個環境cpu使用200%,而另一個使用才20%,所以肯定有異常情況。

   2)通過top監控到總是有幾個執行緒cpu使用率很高,雖然其id在不停的變,但是執行的任務都是在不斷的重連1臺主機。

   3)分析重連主機為什麼消耗這麼多cpu,太浪費了,原來程式碼中使用了Nio的無阻塞連線,但是使用了1個迴圈在等待連線完成,上程式碼:

     while (!this.channel.finishConnect()) {
               判斷是否已經執行2s,否則繼續

      }
        顯然,當網路不可達時,執行緒在這個迴圈裡執行的時間太長了,導致佔用了過多的cpu, 我們在while迴圈的末尾,讓其稍微sleep下就可以了,最後達到的效果幾乎和另一套環境一樣,即使存在網路不可達的情況,也基本不消耗cpu, 心情大好!