JVM 问题排查

CPU使用率高

  • 找出使用率高的进程的pid
top
  • 找出使用率高的线程tpid
top -p pid -H
  • 查看使用率高的线程当前在干什么
jstack -l pid > stack.log
// 将线程的tpid转为16进制,到stack.log中查找
grep tpid stack.log -a3

GC问题

// -t:打印时间戳,1s每隔1秒打印一次
jstat -gcutil -t pid 1s

也可以通过查看gc日志来观察问题

内存泄漏

  1. 执行FullGC后不能回收的内存不断增加
  2. 执行jstat -gcutil pid,查看Old区的使用情况,如果接近100%,则代表内存不足。可以先增大内存,如果还是不断增长到溢出,则考虑是否有内存泄漏问题
  3. 执行jmap -histo:live pid > memory.log,统计所有存活对象的个数,观察那些数量最多的对象,特别是自己写的对象和存放到集合里没有释放的对象
  4. 如果还是无法定位,则执行jmap -dump导出整个Heap,然后使用工具进行分析,注意看自己写的类的依赖关系,看看是不是使用完没有释放,或者一次性查询过多的数据导致内存溢出

线程分析

如果CPU使用率不高,但程序性能低下,则可考虑对线程进行分析,看看各个主要的线程都在做什么,是否有锁争用或IO阻塞问题

为了方便分析,最好给每个线程或者线程池命名

jstack -l pid > stack.log

线程状态

状态 描述
New 线程刚被创建,还没有被执行
Runnable 线程正在执行
Blocked 等待其他线程释放锁
Waiting 调用了wait或join方法,无限等待
Timed_Waiting 调用了sleep、wait(interval)、join(interval)方法,有限等待

观察

死锁

如果JVM发现有死锁存在,会在日志中出现Found one Java-level deadlock

Waiting on condition

在等待一个条件的发生,来把自己唤醒,或者调用了sleep方法
此时线程状态:
WAITING(parking):一直等待那个条件发生
TIMED_WAITING(parking或sleeping):定时等待,即使条件不发生,时间到了也可以自己唤醒

如果发现大量线程处于此状态,并且从线程的堆栈上查看到是正在执行网络读写,这可能是一个网络瓶颈问题或者第三方响应慢的问题

Blocked

线程所需要的资源长时间等待却一直无法获取,被标识为阻塞状态,可以理解为等待资源超时的线程。线程堆栈中一般存在Waiting to Lock

Waiting for monitor entry 和 in Object.wait()

每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是Active Thread,而其它线程都是Waiting Thread,分别在两个队列 Entry Set和Wait Set里面等候。 在Entry Set中等待的线程状态是Waiting for monitor entry,而在Wait Set中等待的线程状态是in Object.wait()。当被调用notify或notifyAll时,只有在Wait Set中的线程会被唤醒

JVM 问题排查_第1张图片

Waiting for monitor entry:等待进入一个临界区 ,所以它在Entry Set队列中等待。此时线程状态一般都是Blocked,如果存在大量线程在此状态,可能是一个全局锁阻塞住了大量线程。随着时间流逝,waiting for monitor entry的线程越来越多,没有减少的趋势,可能意味着某些线程在临界区里呆的时间太长了,以至于越来越多新线程迟迟无法进入临界区

当线程获得了Monitor,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的 wait() 方法,放弃了Monitor,进入Wait Set队列。此时线程状态大致为以下几种:TIMED_WAITING (on object monitor)和 WAITING (on object monitor)

等待IO

有时候线程状态是Runnable,但却是在等待IO

"socketReadThread" prio=6 tid=0x0000000006a0d800 nid=0x1b40 runnable
[0x00000000089ef000] java.lang.Thread.State: RUNNABLE
    at java.net.SocketInputStream.socketRead0(Native Method) 

总结

  1. 如果cpu使用率不高,但性能低下,一般都是由锁或IO阻塞造成,这时要注意查看状态为BLOCKED或者Waiting的线程,看它们需要等待什么锁或者是否出现了死锁,再考虑如何优化并发
  2. 如果发现有大量的线程都在处在 Wait on condition,从线程 stack看,正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。一种情况是网络非常忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达

参考

性能分析之– JAVA Thread Dump 分析综述
三个实例演示 Java Thread Dump 日志分析

你可能感兴趣的:(JVM 问题排查)