輕巧的執行緒堆疊檢視工具HotThreads

青衫無名發表於2017-07-03
定位效能問題,尤其是cpu使用率過高時,經常需要查詢cpu消耗較高的執行緒,然後檢視其堆疊,從而進入程式碼定位問題。
  該場景下, jstack+top是一種非常經典的方式。
  jstack+top
  jstack+top的一般套路:
  1、top -H 檢視cpu佔用較高的執行緒,記錄十進位制的執行緒id
  2、jstack  將執行緒資訊dump到檔案中,在檔案中根據執行緒id查詢該執行緒的堆疊。 注意,jstack輸出中執行緒id是16進位制的,這裡要做一次進位制轉換。
  3、研究這個執行緒的堆疊
  jstack+top方法的不足:
  1、麻煩。由於top工具輸出是實時變化的,一般需要抓多次,重複下來,上述過程更顯繁瑣。
  2、執行緒狀態時刻變動,top -H時看到一個執行緒的cpu佔用率較高,等到jstack 時可能已經處於sleep狀態,因此上述操作需要較高的APM
  有什麼辦法能省卻這些麻煩 —— 能在看到執行緒堆疊的時候,直接看到他們各自的cpu佔用率呢? —— Hot Threads 可以!
  Hot Threads
  Hot Threads是一個小巧的開源工具,使用十分容易:
  1、下載jar包,扔到伺服器上
  2、執行java -jar HotThread.jar [pid] 即可,pid是被測的程式號。使用中注意填對路徑即可。
  Hot Threads的輸出:
  執行完上述指令後,Hot Threads會在很短時間內,重複查詢10次執行緒堆疊資訊(呼叫sun.management.ThreadImpl.getThreadInfo方法),統計平均cpu佔用最高的3個執行緒,列印執行緒堆疊,並顯示cpu佔用率。
106.3% CPU Usage by Thread `Swing-Shell`
10/10 snapshots sharing following 10 elements
sun.awt.shell.Win32ShellFolder2.getAttributes0(Native Method)
sun.awt.shell.Win32ShellFolder2.access$600(Unknown Source)
sun.awt.shell.Win32ShellFolder2$6.call(Unknown Source)
sun.awt.shell.Win32ShellFolder2$6.call(Unknown Source)
java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
java.util.concurrent.FutureTask.run(Unknown Source)
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
sun.awt.shell.Win32ShellFolderManager2$ComInvoker$3.run(Unknown Source)
java.lang.Thread.run(Unknown Source)
1.6% CPU Usage by Thread `RMI TCP Connection(9)-172.30.41.210`
10/10 snapshots sharing following 32 elements
sun.management.ThreadImpl.getThreadInfo0(Native Method)
sun.management.ThreadImpl.getThreadInfo(Unknown Source)
sun.reflect.GeneratedMethodAccessor106.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
com.sun.jmx.mbeanserver.ConvertingMethod.invokeWithOpenReturn(Unknown Source)
com.sun.jmx.mbeanserver.MXBeanIntrospector.invokeM2(Unknown Source)
com.sun.jmx.mbeanserver.MXBeanIntrospector.invokeM2(Unknown Source)
com.sun.jmx.mbeanserver.MBeanIntrospector.invokeM(Unknown Source)
com.sun.jmx.mbeanserver.PerInterface.invoke(Unknown Source)
com.sun.jmx.mbeanserver.MBeanSupport.invoke(Unknown Source)
javax.management.StandardMBean.invoke(Unknown Source)
com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(Unknown Source)
com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(Unknown Source)
javax.management.remote.rmi.RMIConnectionImpl.doOperation(Unknown Source)
javax.management.remote.rmi.RMIConnectionImpl.access$200(Unknown Source)
javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(Unknown Source)
javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(Unknown Source)
javax.management.remote.rmi.RMIConnectionImpl.invoke(Unknown Source)
sun.reflect.GeneratedMethodAccessor21.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
sun.rmi.transport.Transport$1.run(Unknown Source)
java.security.AccessController.doPrivileged(Native Method)
sun.rmi.transport.Transport.serviceCall(Unknown Source)
sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
java.lang.Thread.run(Unknown Source)
0.0% CPU Usage by Thread `Reference Handler`
10/10 snapshots sharing following 3 elements
java.lang.Object.wait(Native Method)
java.lang.Object.wait(Object.java:485)
java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)

  上圖中,每個執行緒的能看到 10/10 標記。 n / m 意味著m次統計中,有n次都是該排名。

  Hot Threads使用中的問題和解決方法:
  直接執行java -jar HotThread.jar [pid]可能會報錯,此時可以換一種啟動方式:
  java -classpath “/opt/jdk1.6/lib/tools.jar:HotThread.jar” hotthread.Main [pid]
  其中 /opt/jdk1.6/lib/tools.jar 是伺服器上jdk tools包的完整路徑,hotthread.Main 是Hot Threads程式的入口。
  Hot Threads的不足:
  使用中發現,Hot Threads自身的cpu開銷比較高,有時候統計顯示cpu使用率第一的執行緒,在執行的是獲取執行緒資訊的操作,該條堆疊對分析問題無效。
  2723.0% CPU Usage by Thread `RMI TCP Connection(4)-192.168.164.87`
  6/10 snapshots sharing following 33 elements
  sun.management.ThreadImpl.getThreadInfo1(Native Method)
  sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:154)
  sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
  總結:
  Hot Threads使用快速輕巧, 在需要使用jstack + top的場合,都可以嘗試代替比較繁瑣的jstack + top。


最新內容請見作者的GitHub頁:http://qaseven.github.io/


相關文章