分析java程序中cpu占用过高的线程

来源:互联网 发布:mac os x 输入法切换 编辑:程序博客网 时间:2024/05/14 12:56

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 5683top - 09:14:06 up 270 days, 18:33,  8 users,  load average: 7.94, 9.70, 10.31Tasks:  48 total,   2 running,  46 sleeping,   0 stopped,   0 zombieCpu(s): 20.4% us, 30.5% sy,  0.0% ni, 43.8% id,  5.4% wa,  0.0% hi,  0.0% siMem:  16625616k total, 16498560k used,   127056k free,    22020k buffersSwap: 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导致



原创粉丝点击