記錄一次線上使用jvisualvm排查問題的過程

背景:最近測試批量任務執行的時候發現服務器端的線程數時不時的會出現飆升,而且執行完以後不見降低。要知道出現多線程問題是比較頭疼的,由於有時候一味的跟代碼不必定能看出問題,還比較耗時。這時候就須要用到一些工具來監控線程的執行過程和狀態,JDK自身提供了提供了不少工具來監控jvm運行狀態,這些工具都放在bin目錄下。這裏主要使用的是jvisualvm工具來監控線程,並經過導出的線程dump文件分析問題所在。java

一.linux上使用jvisualvm的方式

      jvisualvm打開是一個圖形化界面linux


    如圖所示,該軟件能夠監控本地的java進程的CPU,類,線程,的消耗狀況,點擊線程dump也能夠比較方便的查看當前的dump日誌。固然還有其餘功能也支持插件暫時沒涉到,之後慢慢摸索。
windows

    能夠看到有Local和Remote兩種方式打開,這裏介紹下用SecureCRT遠程鏈接Linux 使用X11 轉發功能打開圖形化窗口:bash

    1.檢查/etc/ssh/sshd_config文件,確保如下參數正確:服務器

AllowTcpForwarding yes
##啓用X11 
ForwardingX11Forwarding yes
X11UseLocalhost no複製代碼

    2.重啓sshd服務:多線程

service sshd restart複製代碼

    3.檢查下linux服務器上是否安裝了xorg-x11-xauth,若是沒有則須要先安裝xauth,命令以下:ssh

yum install xorg-x11-xauth複製代碼

   4.在windows主機安裝Xming, 啓動X serverjvm

下載地址:工具

sourceforge.net/projects/xm…
下載安裝完直接啓動就行。post

    5.安裝SecureCRT,並配置對應會話的轉發X11

SecureCRT有綠色版直接解壓打開便可,配置對應會話的轉發X11以下圖:


注意:配置完以後要從新打開會話鏈接。

    6.在SecureCRT創建的Linux會話界面cd到jdk安裝路徑的bin目錄下直接啓動

./jvisualvm複製代碼

二.分析根據線程dump文件分析緣由

經過分析下載下來的dump文件發現出現大量的相似如下的信息:

"pool-16-thread-55" #767 prio=5 os_prio=0 tid=0x00007f4a90035000 nid=0x13e6 waiting on condition [0x00007f4960eb8000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000005cfbdc670> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
	at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None複製代碼

大體介紹下上面的線程info信息快的內容

1. "pool-16-thread-55" #767 prio=5 os_prio=0 tid=0x00007f4a90035000 nid=0x13e6 waiting on condition [0x00007f4960eb8000]
# 線程名稱:pool-16-thread-55;優先級: 5,默認是5;# JVM線程id:tid=0x00007f4a90035000,JVM內部線程的惟一標識(經過java.lang.Thread.getId()獲取,一般用自增方式實現)。# 對應系統線程id(NativeThread ID):nid=0x13e6 ,和top命令查看的線程pid對應,不過一個是10進制,一個是16進制。(經過命令:top -H -p pid,能夠查看該進程的全部線程信息)# 線程狀態:waiting on condition 
# 起始棧地址:[0x00007f4960eb8000],對象的內存地址,經過JVM內存查看工具,可以看出線程是在哪兒個對象上等待;2.java.lang.Thread.State: WAITING (parking)
3.at sun.misc.Unsafe.park(Native Method)
4.- parking to wait for  <0x00000005cfbdc670> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
5.at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
6.at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)--等待任務進入
7.at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
8.at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)--獲取執行任務
9.at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)--線程池運行
10.at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
11.at java.lang.Thread.run(Thread.java:748)複製代碼

    由上面線程信息塊能夠發現的出結論大量的線程都處於waiting狀態。而且引發waiting的緣由是由於線程到任務隊列獲取任務時沒獲取到任務,致使線程被park住。也就是任務都執行完了可是線程還沒被回收,緣由多是建立線程池時設置的核心線程數過大;也有可能建立了多個線程池實例,每次使用不一樣的線程池實例運行任務致使。接下來就是去對應的地方分析代碼實現(ps:看別人的代碼是真是一個痛苦的過程),經過分析代碼發現每一個服務都建立了對應的線程池去批量處理,並且每次添加新流程發佈的時候都會從新建立一個新的流程線程池去執行服務。到這裏問題的緣由找到了,接下來就是修改代碼了。

   分析線程dump文件時對於線程狀態和爲何會產生該狀態的緣由要有必定的瞭解,否則即便有了線程信息也會一臉懵逼的。

具體的線程狀態和產生緣由能夠參考下面的文章

juejin.im/post/5b31b5…

相關文章
相關標籤/搜索