一、thread dump
产品是Java写的,运行于JBOSS平台。操作系统为linux。当系统出现问题是很,可以通过thread dump分析就可以找到,但这只是解决问题的第一步,即找到问题的所在。
首先:如何产生thread dump日志?
第一步:找到应用程序所在的进 程号,通过top命令可以找到,不详述。
第二步:执行kill -3 pid获取thread dump日志(pid就是第一步获取到的)。注意:在不同的linux环境下执行输出的日志的地方可能不同。在IBM的PowerPC小型机上的linux上执行kill -3 pid会在工作目录下产生类似javacore.20100409.161739.7614.0001.txt的文件。而在我所在的环境中,thread dump信息输出到JBOSS的日志文件中的。
其次:获取线程信息
大多数服务器应用都是多线程, 因此必须查到具体是哪些线程占用的CPU高。通过top –H命令可以查看到应用程序的线程信息及占用CPU的情况。
PID所在的列即是对应的线程ID,这是十进制的。
最后:找到耗费CPU高的线程及对应的源代码
取上面耗费CPU最高的第一行的PID 4280,将其转化为十六进制得到0x10b8。然后在thread dump日志中搜索0x10b8,将会搜到如下信息:
"Stack.ClientSelector-1" daemon prio=10 tid=0x000000004baeec00 nid=0x10b8 runnable [0x0000000053169000..0x0000000053169c90]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(Unknown Source)
at sun.nio.ch.EPollSelectorImpl.doSelect(Unknown Source)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(Unknown Source)
- locked <0x00002aaac4105468> (a sun.nio.ch.Util$1)
- locked <0x00002aaac4131670> (a java.util.Collections$UnmodifiableSet)
- locked <0x00002aaac3f79c78> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(Unknown Source)
at com.*****.warlock.protocolstack.impl.layer2.nio.ActiveSelectorImpl.callSelect(ActiveSelectorImpl.java:288)
at com. *****.warlock.protocolstack.impl.layer2.nio.ActiveSelectorImpl.run(ActiveSelectorImpl.java:163)
上面日志中的nid即是线程号。这样可以清晰的看到耗费CPU的源代码的具体位置,可以精确到行号。
结束语:thread dump的作用远不只一点,比如还可以从中发现很多应用程序运行问题,比如死锁等。对于该日志还有一个Eclipse插件的可视化分析工具lockness,我试了一下感觉不错。大家可以参考(3)。
二、Java多线程的监控分析工具(VisualVM)
在java多线程程序运行时,多数情况下我们不知道到底发生了什么,只有出了错误的日志的时候,我们才知道原来代码中有死锁。撇开代码检查工具,我 们先讨论一下利用VisualVM监控,分析我们的多线程的运行情况。(注: 实践本文内容的JDK的版本需要1.6.07以上)
什么是VisualVM
VisualVM是JDK的一个集成的分析工具,自从JDK 6 Update 7以后已经作为Sun 的JDK的一部分。
VisualVM可以做的:监控应用程序的性能和内存占用情况、监控应用程序的线程、进行线程转储(Thread Dump)或堆转储(Heap Dump)、跟踪内存泄漏、监控垃圾回收器、执行内存和CPU分析,保存快照以便脱机分析应用程序;同时它还支持在MBeans上进行浏览和操作。尽管 VisualVM自身要在JDK6以上的运行,但是JDK1.4以上版本的程序它都能被它监控。
在JDK1.6.07以上的版本中:到$JAVA_HOME/bin,点击jvisualvm.exe图标就可以启动VisualVM;当然也可以点击这里获取官方的最新版本,支持:英文,中文,日文。
VisualVM功能集成较多,我们这里只讨论它对象线程的监控分析。
VisualVM监控线程
当我们运行VisualVM的时候,可以在应用程序》本地中看到VisualVM和eclipse的运行程序,然后我们启动eclipse中的一个线 程:com.longtask.thread.TestVisualVm,可以看到在菜单中多了一个该线程的显示。点击右边的 线程 菜单,可以看到线程运行的跟踪情况。
点击 thread dump,可以生成该线程的运行情况的tdump文件,通过thread dump提供的相关信息,我们可以看到线程在什么地方被阻塞了以及线程的其他状态。
把日志另存为文件,到Thread Dump Analyzer的主页点击图标下载TDA,然后用TDA打开刚才VisualVM保存 的 thread dump文件,查看相关的分析结果。
我们也可以用VisualVM来监控远程java线程的运行情况。
远程监控:启动RMI服务
1:新建一个jstatd.all.policy文件,在里面添加以下内容来保证jstatd服务启动的时候不报异常:
grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};
2:netstat -ano | grep -i 1099 查看1099端口是否被占用了,如果被占用,则需要选择其他端口来启动jstatd服务
3:如果端口被占用,用以下方式启动jstatd服务:
rmiregistry 2020 & jstatd -J-Djava.security.policy=jstatd.all.policy -p 2020
更多jstatd的文档请参考sun公司的官方文档 这里
远程监控Jboos服务
1:修改JDK下面的jmx的配置文件:
切换至$JAVA_HOME所在目录/jre/lib/management下,
I:将jmxremote.access、jmxremote.password.template权限调整为读写:
#cp jmxremote.password.template jmxremote.password
#chmod 600 jmxremote.password
#chmod 600 jmxremote.access
II:vi jmxremote.password去掉
# monitorRole QED
# controlRole R&D
的#号
2:在Jboss的启动文件中添加以下信息:
JAVA_OPTS="-Dcom.sun.management.jmxremote.port=2899 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false \
-Djava.rmi.server.hostname=10.212.20.9 其他配置"
3:检查启动情况:
netstat -a | grep -i 2899 查看端口占有情况
如果2899端口被其他程序占用,在jboss配置文件中调整端口-Dcom.sun.management.jmxremote.port=****
而后在VisualVM中就添加远程连接,选择jmx方式,就可以监控jboss的运行情况了。
三、java常用工具介绍
jstatd
启动jvm监控服 务。它是一个基于rmi的应用,向远程机器提供本机jvm应用程序的信息。默认端口1099。
实例:jstatd -J-Djava.security.policy=my.policy
my.policy文件需要自己建立,内如如下:
grant codebase "file:$JAVA_HOME/lib/tools.jar" {
permission java.security.AllPermission;
};
这是安全策略文件,因为jdk对jvm做了jaas的安全检测,所以我们 必须设置一些策略,使得jstatd被允许作网络操作
jps
列出所有的jvm实例
实 例:
jps
列出本机所有的jvm实例
jps 192.168.0.77
列出远程服务器192.168.0.77 机器所有的jvm实例,采用rmi协议,默认连接端口为1099
(前提是远程服务器提供jstatd服务)
输出内容如下:
jones@jones:~/data/ebook/java/j2se/jdk_gc$ jps
6286 Jps
6174 Jstat
jconsole
一个图形化界面,可以观察到java进程的 gc,class,内存等信息。虽然比较直观,但是个人还是比较倾向于使用jstat命令(在最后一部分会对jstat作详细的介绍)。
jinfo(linux下特有)
观 察运行中的java程序的运行环境参数:参数包括Java System属性和JVM命令行参数
实例:jinfo 2083
其中2083 就是java进程id号,可以用jps得到这个id号。
输出内容太多了,不在这里一一列举,大家可以自己尝试这个命令。
jstack(linux下特有)
可 以观察到jvm中当前所有线程的运行情况和线程当前状态
jstack 2083
jmap(linux下特有,也是很常用 的一个命令)
观察运行中的jvm物理内存的占用情况。
参数如下:
-heap:打印jvm heap的情况
-histo:打印jvm heap的直方图。其输出信息包括类名,对象数量,对象占用大小。
-histo:live :同上,但是只答应存活对象的情况
-permstat:打印permanent generation heap情况
命令使用:
jmap -heap 2083
可以观察到New Generation(Eden Space,From Space,To Space),tenured generation,Perm Generation的内存使用情况
jmap -histo 2083 | jmap -histo:live 2083
可以观察heap中所有对象的情况(heap中所有生存的对 象的情况)。包括对象数量和所占空间大小。
写个脚本,可以很快把占用heap最大的对象找出来,对付内存泄漏特别有效。
jstat
最后要重点介绍下这个 命令。
这是jdk命令中比较重要,也是相当实用的一个命令,可以观察到classloader,compiler,gc相关信息
具体参数 如下:
-class:统计class loader行为信息
-compile:统计编译行为信息
-gc:统计jdk gc时heap信息
-gccapacity:统计不同的generations(不知道怎么翻译好,包括新生区,老年区,permanent区) 相应的heap容量情况
-gccause:统计gc的情况,(同-gcutil)和引起gc的事件
-gcnew:统计gc时,新生代的情 况
-gcnewcapacity:统计gc时,新生代heap容量
-gcold:统计gc时,老年区的情况
-gcoldcapacity: 统计gc时,老年区heap容量
-gcpermcapacity:统计gc时,permanent区heap容量
-gcutil:统计 gc时,heap情况
-printcompilation:不知道干什么的,一直没用过。
一般比较常用的几个参数是:
jstat -class 2083 1000 10 (每隔1秒监控一次,一共做10次)
输出内容含义如下:
Loaded | Number of classes loaded. |
Bytes | Number of Kbytes loaded. |
Unloaded | Number of classes unloaded. |
Bytes | Number of Kbytes unloaded. |
Time | Time spent performing class load and unload operations. |
jstat -gc 2083 2000 20(每隔2秒监控一次,共做10)
输出内容含义如下:
S0C | Current survivor(存活的) space 0 capacity (KB). |
EC | Current eden space capacity (KB). |
EU | Eden space utilization (KB). |
OC | Current old space capacity (KB). |
OU | Old space utilization (KB). |
PC | Current permanent space capacity (KB). |
PU | Permanent space utilization (KB). |
YGC | Number of young generation GC Events. |
YGCT | Young generation garbage collection time. |
FGC | Number of full GC events. |
FGCT | Full garbage collection time. |
GCT | Total garbage collection time. |
监控内存使用情况 参数 (查看内存溢出相对有用)
jstat -gccause 2083 5000 (每隔5秒监控一次)
输出内容含义如下:
S0 | Survivor space 0 utilization as a percentage of the space's current capacity. |
S1 | Survivor space 1 utilization as a percentage of the space's current capacity. |
E | Eden space utilization as a percentage of the space's current capacity. |
O | Old space utilization as a percentage of the space's current capacity. |
P | Permanent space utilization as a percentage of the space's current capacity. |
YGC | Number of young generation GC events. |
YGCT | Young generation garbage collection time. |
FGC | Number of full GC events. |
FGCT | Full garbage collection time. |
GCT | Total garbage collection time. |
LGCC | Cause of last Garbage Collection. |
GCC | Cause of current Garbage Collection. |