您的位置:首页 > 编程语言 > Java开发

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

2013-09-27 08:35 585 查看
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代码



<span style="font-size: medium;">Unable to open socket file: target process not responding or HotSpot VM not loaded

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代码



<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

be used. The default temporary-file directory is specified by the system property java.io.tmpdir.

On UNIX systems the default value of this property is typically "/tmp" or "/var/tmp";

on Microsoft Windows systems it is typically "c:\\temp". A different value may be given to this

system property when the Java virtual machine is invoked, but programmatic changes to this

property are not guaranteed to have any effect upon the the temporary directory used by this

method</span>



</span>



其次看下jvmstat使用的前提

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

Java代码



<span style="font-size: medium;">If the error message is of the form:

Could not attach to vmid: reason

The jvmstat tools can only monitor the HotSpot 1.4.1 JVM and only when the target JVM has been

started with the -XX:+UsePerfData option. Please verify that your application is running with the

correct JVM and that the required -XX option is set. </span>



<span style="font-size: medium;">

</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导致
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: