分析java程序中cpu佔用過高的線程

http://www.cnblogs.com/skyaccross/archive/2012/12/22/2829000.html



收到服務器報警,服務端的一個java服務佔用cpu200%多。該服務裏面跑了很多線程,於是想找到是誰引起的
1、首先dump出該進程的所有線程及狀態
使用命令 jstack PID 命令打印出CPU佔用過高進程的線程棧.

jstack -l 5683 > 5683.stack

將進程id爲5683的線程棧輸出到了文件

2、使用top命令找到耗cpu的線程
    使用top -H -p PID 命令查看對應進程是哪個線程佔用CPU過高.

複製代碼
[goocar@LoginSVR ~]$ top -H -p 5683
top - 09:14:06 up 270 days, 18:33,  8 users,  load average: 7.94, 9.70, 10.31
Tasks:  48 total,   2 running,  46 sleeping,   0 stopped,   0 zombie
Cpu(s): 20.4% us, 30.5% sy,  0.0% ni, 43.8% id,  5.4% wa,  0.0% hi,  0.0% si
Mem:  16625616k total, 16498560k used,   127056k free,    22020k buffers
Swap: 16771820k total,  9362112k used,  7409708k free,  2224132k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                
 5728 ecar    16   0 2442m 1.3g 288m R 38.3  8.4 208:06.62 java                                                                   
 5726 ecar    16   0 2442m 1.3g 288m S 37.3  8.4 209:08.91 java                                                                   
 5727 ecar    16   0 2442m 1.3g 288m R 37.3  8.4 213:14.04 java                                                                   
 5729 ecar    16   0 2442m 1.3g 288m S 35.6  8.4 211:39.23 java                                                                   
 5683 ecar    16   0 2442m 1.3g 288m S  0.0  8.4   0:00.00 java                                                                   
 5685 ecar    18   0 2442m 1.3g 288m S  0.0  8.4   0:01.62 java                                                                   
 5686 ecar    16   0 2442m 1.3g 288m S  0.0  8.4  21:13.33 java    
複製代碼

可以看到是  5726 ~ 5729這4個線程佔用的cpu比較高

3. 將線程的pid 轉成16進制,比如5729 = 0x1661
   到第一步dump出來的 5683.stack 裏面找0x1661 就知道是哪個線程了

複製代碼
   "Server-3" prio=10 tid=0x6f1fc000 nid=0x1661 runnable [0x6d67f000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.FileDispatcher.write0(Native Method)
    at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:29)
    at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:104)
    at sun.nio.ch.IOUtil.write(IOUtil.java:60)
    at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:334)
    - locked <0x77f3b3c0> (a java.lang.Object)
    at java.lang.Thread.run(Thread.java:619)
複製代碼

   經過查找,是服務線程比較忙,初步解決方法,就是加大服務線程數,重啓,問題解決。



線上遇到一個問題:


Java代碼  收藏代碼
  1. <span style="font-size: medium;">Unable to open socket file: target process not responding or HotSpot VM not loaded  
  2. The -F option can be used when the target process is not responding</span>  

重啓後解決,主要原因是pid文件沒有生成,這裏記錄下相關的問題點。


首先解釋下  java.io.tmpdir

見 http://download.oracle.com/javase/1.4.2/docs/api/java/io/File.html


Java代碼  收藏代碼
  1. <span style="font-size: x-small;">  <span style="font-size: medium;"> If the directory argument is null then the system-dependent default temporary-file directory will   
  2. be used. The default temporary-file directory is specified by the system property java.io.tmpdir.  
  3. On UNIX systems the default value of this property is typically "/tmp" or "/var/tmp";   
  4. on Microsoft Windows systems it is typically "c:\\temp". A different value may be given to this   
  5. system property when the Java virtual machine is invoked, but programmatic changes to this  
  6. property are not guaranteed to have any effect upon the the temporary directory used by this  
  7. method</span>  
  8.   
  9. </span>  

 

其次看下jvmstat使用的前提

見 http://java.sun.com/performance/jvmstat/faq.html#3


Java代碼  收藏代碼
  1. <span style="font-size: medium;">If the error message is of the form:  
  2.       Could not attach to vmid: reason  
  3. The jvmstat tools can only monitor the HotSpot 1.4.1 JVM and only when the target JVM has been   
  4. started with the -XX:+UsePerfData option. Please verify that your application is running with the   
  5. correct JVM and that the required -XX option is set. </span>  
  6.   
  7. <span style="font-size: medium;">  
  8. </span>  

 

接着來說下上面2者的關係,jvmstat會生成一個目錄文件叫hsperfdata_username,那這個目錄文件在哪裏呢,默認的是生成在 java.io.tmpdir目錄下, java.io.tmpdir在linux下默認是/tmp下,故默認開啓了jvm monitor的功能以後就會在/tmp目錄下生成一個目錄叫 hsperfdata_username ,然後這個目錄中會有一個pid文件,可以利用strings查看裏面的文件內容,一般就是jvm的進程信息而已。


知道了這個所以我們可以自己修改這個目錄位置,防止被刪除而導致一些依賴這個目錄的程序出現問題。

Djava.io.tmpdir=/home/admin/xxxxxx


看下之前別人給sun的一個bug report與該問題相關的


JVM creates subdirectory "hsperfdata_xxx"

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5012932


temp dir locations should not be hardcoded for hsperfdata_<USER> dirctories

http://bugs.sun.com/view_bug.do?bug_id=6447182



針對jdk更新帶出的問題 ,參見一個blog說明


Java update breaks jps, jconsole, etc


http://underlap.blogspot.com/2011/03/java-update-breaks-jps-jconsole-etc.html



由此問題進一步引申開來就會發現以下幾點容易出問題的地方:


1:沒有生成 hsperfdata_xxx目錄報錯

2:生成 hsperfdata_xxx目錄但是沒有pid文件

3:沒權限生成,例如指定的/tmp或者其他目錄不可 寫,包括2種情況,一是沒權限,而是空間不夠

4:文件不可讀,例如2個不同的用戶來操作這個文件,一般是A用戶啓動java  B用戶來運行jvmstat等等

5:java生成的目錄 和 jvmstat 使用的目錄文件不是同一個,這個可以見上面jdk update引起的bug導致



發佈了123 篇原創文章 · 獲贊 15 · 訪問量 58萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章