1.jstack常用的方式
在实际运行中,往往一次 dump的信息,还不足以确认问题。建议产生三次 dump信息,如果每次 dump都指向同一个问题,我们才确定问题的典型性。
命令格式:$jstack [ option ] pid
程序中某个服务占用cpu较高。用jstack+top命令查找占用cpu资源最多的线程
1)用top命令,查看那个服务占的cpu资源比较高(按C键显示详细),找到pid.
2)查找到java的进程id为’12377’,接下来用top命令单独对这个进程中的所有线程作监视:top -p 12377 -H
3)要想找到到底是哪段具体的代码占用了如此多的资源,先使用jstack打出当前栈信息到一个文件里, 比如stack.log:
jstack 12377 > stack.log (这里的12377可以是任意java PID)
注意:jstack命令必须由当前运行的java进程用户执行(即root用户),否则会出现“12377: Operation not permitted”错误
2.jstack打印出的日志信息
"qtp496432309-42" prio=10 tid=0x00002aaaba2a1800 nid=0x7580 waiting on condition [0x00000000425e9000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000788cfb020> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
Locked ownable synchronizers:
- None
从上述的代码示例中我们可以看到该用户线程的以下几类信息: 线程的状态、线程的调用情况、线程对资源的锁定情况
线程的状态分析:
该状态表示线程具备所有运行条件,在运行队列中准备操作系统的调度,或者正在运行。
如果发现有大量的线程都在处在 Wait on condition,从线程 stack看, 正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。一种情况是网络非常忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。所以要结合系统的一些性能观察工具来综合分析,比如 netstat统计单位时间的发送包的数目、 观察 cpu的利用率,系统态的CPU时间相对于用户态的CPU时间比例等;
另外一种出现 Wait on condition的常见情况是该线程在 sleep,等待 sleep的时间到了时候,将被唤醒。
- Waiting for monitor entry 和 in Object.wait()
synchronized(obj) {
.........
}
如果锁对象被其它线程拥有,本线程在“Entry Set”队列中等待,此时dump的信息显示“waiting for monitor entry”。如果本线程获取到了锁,执行临界区的代码 ,此时线程将处于Runnable状态。
synchronized(obj){
.........
obj.wait();
.........
}
当线程获得了锁,进入了临界区之后,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的 wait() 方法,放弃了锁,进入 “Wait Set”队列,此时 dump的信息中显示为:in Object.wait()。只有当别的线程在该对象上调用了 notify() 或者 notifyAll() , “ Wait Set”队列中线程才得到机会去竞争锁,但是只有一个线程获得对象的锁,然后恢复到运行态Runnable。
3.死锁的日志形式
在 JAVA 5中加强了对死锁的检测。线程 Dump中可以直接报告出 Java级别的死锁,如下所示:
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x0003f334 (object 0x22c19f18, a java.lang.Object), which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x0003f314 (object 0x22c19f20, a java.lang.Object), which is held by "Thread-1"
4.visualvm的远程使用的配置方式
tomcat/bin/catalina.sh文件,hostname改成自己Ip.
CATALINA_OPTS="$JAVA_OPTS
-Djava.rmi.server.hostname=172.16.1.20
-Dcom.sun.management.jmxremote.port=9527
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false"
按照如上配置的话,在visualvm输入的第地址就是172.16.1.20:9527