美文网首页
JVM 问题排查

JVM 问题排查

作者: PennyWong | 来源:发表于2016-05-31 15:10 被阅读1166次

    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中的线程会被唤醒

    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 问题排查

          本文链接:https://www.haomeiwen.com/subject/qtgcdttx.html