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日志来观察问题
内存泄漏
- 执行FullGC后不能回收的内存不断增加
- 执行jstat -gcutil pid,查看Old区的使用情况,如果接近100%,则代表内存不足。可以先增大内存,如果还是不断增长到溢出,则考虑是否有内存泄漏问题
- 执行jmap -histo:live pid > memory.log,统计所有存活对象的个数,观察那些数量最多的对象,特别是自己写的对象和存放到集合里没有释放的对象
- 如果还是无法定位,则执行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中的线程会被唤醒
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)
总结
- 如果cpu使用率不高,但性能低下,一般都是由锁或IO阻塞造成,这时要注意查看状态为BLOCKED或者Waiting的线程,看它们需要等待什么锁或者是否出现了死锁,再考虑如何优化并发
- 如果发现有大量的线程都在处在 Wait on condition,从线程 stack看,正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。一种情况是网络非常忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达