监控java线程

    在上一篇文章《java多线程和多核心测试》中,我们大致能分析出线程应该占有多少cpu资源,但是,程序可能并没有按我们预想的去运行,这样,我们有时候需要能够有效的监控它。好在jdk的devel包已经带了很多的监控工具来完成这个任务。

    1)获取运行程序的进程id, 可以通过jps或者ps 过滤出来。

    2)通过top -p pid来查看该进程总的资源使用情况

    3)通过jstack -l pid, 能实时打印出某个时刻所有线程的运行堆栈,包括每个线程的名字和id,id和名字的对应关系是不会变的。

    4)  通过top -H -p pid 来实时看到每个线程的cpu使用情况,如果某个线程异常的占有很多cpu, 我们很快就可以识别出来。


     根据线程Id定位线程

     有了上面的工具加上程序的日志,完全可以定位到每个线程的资源使用情况。但是还是会遇到一些问题:

     1,如何根据线程的名字找到线程

     这个其实很简单,在top -H -p里看到某个线程一直占用很多cpu,如id为7593,转成16进制为0x1da9, 那么可以在jstack -l 里通过这个id 找到这个线程在干什么,如jstack -l输出:

     "SSL Stomp Reactor" daemon prio=10 tid=0x00007fa3e40ac800 nid=0x1da9 runnable [0x00007fa4b2bed000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
        at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
        at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:79)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
        - locked <0x0000000608e5db80> (a sun.nio.ch.Util$2)
        - locked <0x0000000608e5db90> (a java.util.Collections$UnmodifiableSet)
        - locked <0x0000000608e5db38> (a sun.nio.ch.EPollSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
        at org.ovirt.vdsm.jsonrpc.client.reactors.Reactor.select(Reactor.java:47)
        at org.ovirt.vdsm.jsonrpc.client.reactors.Reactor.run(Reactor.java:59)

   Locked ownable synchronizers:
        - None
      我们不仅知道了这个线程在干什么,而且还知道了该线程的名字,而java的日志输出里一般都有线程的名字,所以,我们可以通过日志进一步知道线程在做什么,然后再分析是哪块逻辑出了问题。

        

    2,使用了线程池,线程执行的任务是不停的变换的,导致用top -H -P 看的时候,线程的id一直在不停的变

    跟上面类似,我们只能抓取某个时刻使用cpu较多的线程id, 但是我们再用jstack去扫描线程的时候,可能这时候这个线程已经被调度去干别的任务了,所以得再想想变通的办法。

    我们知道线程池里的线程可以调度干不同的任务,但是其id和名字的对应关系还是不会变的,我们抓取到瞬时某个cpu的异常情况后,可以找到这个线程的名字,然后再去日志里面查看这个线程的所有日志,就知道这个线程最近都干了些啥,很可能我们抓取了不同时刻3个线程出现异常,而这三个线程干的其实是同样的一个任务,这样我们就能锁定目标了。


   案例分享:

   1)在监控中,我们发现有两套相同的环境,负载也差不多,却发现一个环境cpu使用200%,而另一个使用才20%,所以肯定有异常情况。

   2)通过top监控到总是有几个线程cpu使用率很高,虽然其id在不停的变,但是执行的任务都是在不断的重连1台主机。

   3)分析重连主机为什么消耗这么多cpu,太浪费了,原来代码中使用了Nio的无阻塞连接,但是使用了1个循环在等待连接完成,上代码:

     while (!this.channel.finishConnect()) {
               判断是否已经运行2s,否则继续

      }
        显然,当网络不可达时,线程在这个循环里运行的时间太长了,导致占用了过多的cpu, 我们在while循环的末尾,让其稍微sleep下就可以了,最后达到的效果几乎和另一套环境一样,即使存在网络不可达的情况,也基本不消耗cpu, 心情大好!





你可能感兴趣的:(java性能)