快速查看占用CPU前五的java线程栈

2016-09-06

这个shell脚本,是在Github上发现的,有人把一些有用的脚本整理成了useful-scripts 但是我发现一个问题,这么多脚本放一起,大家反而关注不了真正效率提升的这个show-busy-java-thrads,其他的类似svn的一些脚本都太陈旧了,并不适合推荐。这里单独抽出这个分析线程栈的脚本,非常适合用于开发/线上环境 一眼就能看出来是哪一个程序在搞怪。

用于快速排查Java的CPU性能问题(top usage值过高),自动查出运行的Java进程中消耗CPU多的线程,并打印出其线程栈,从而确定导致性能问题的方法调用。

PS,如何操作可以参见@bluedavy 的《分布式Java应用》的【5.1.1 cpu消耗分析】一节,说得很详细:

top命令找出有问题Java进程及线程id:

  • 开启线程显示模式
  • 按CPU使用率排序
  • 记下Java进程id及其CPU高的线程id
  • 用进程id作为参数,jstack有问题的Java进程
  • 手动转换线程id成十六进制(可以用printf %x 1234)
  • 查找十六进制的线程id(可以用grep)
  • 查看对应的线程栈

查问题时,会要多次这样操作以确定问题,上面过程太繁琐太慢了。

Usage

wget https://raw.githubusercontent.com/iqiancheng/fast-profiler/master/show-busy-java-threads.sh
sudo chmod u+x show-busy-java-threads.sh
sudo ./show-busy-java-threads.sh

demo

[root@localhost ~]# ./show-busy-java-threads.sh
[1] Busy(0.3%) thread(2903/0xb57) stack of java process(2886) under user(root):
"NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2182" daemon prio=10 tid=0x00007f64b023b000 nid=0xb57 runnable [0x00007f64a4844000]
   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 <0x00000000c4216a40> (a sun.nio.ch.Util$2)
        - locked <0x00000000c4216a30> (a java.util.Collections$UnmodifiableSet)
        - locked <0x00000000c42168a8> (a sun.nio.ch.EPollSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
        at org.apache.zookeeper.server.NIOServerCnxnFactory.run(NIOServerCnxnFactory.java:178)
        at java.lang.Thread.run(Thread.java:745)

[2] Busy(0.2%) thread(2946/0xb82) stack of java process(2886) under user(root):
"CommitProcessor:2" prio=10 tid=0x00007f645801c800 nid=0xb82 in Object.wait() [0x00007f649fffe000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:503)
        at org.apache.zookeeper.server.quorum.CommitProcessor.run(CommitProcessor.java:80)
        - locked <0x00000000c53d84e8> (a org.apache.zookeeper.server.quorum.CommitProcessor)

...

是不是很好用呢!

更多用法

./show-busy-java-threads.sh -c3

-c 的参数表示显示的线程数

./show-busy-java-threads.sh -c3 -p2886

表示指定某个pid的进程,并显示最多3个线程的详情。

./show-busy-java-threads.sh -p2886

表示指定某个pid的进程。这里2886 是一个示例pid.

TroubleShooting

如果执行的时候遇到

说明当前用户下的sudo which jstack 找不到在usr/sbin下的jstack命令,可以通过下面的命令修复

$ sudo ln -s /usr/java/jdk1.8.0_101/bin/jstack /usr/sbin/jstack

记得把java安装目录替换成自己实际的安装目录。

如果遇到说用户不匹配的问题,这个是由于脚本本身的bug造成的,于是我fix了这个bug

对用户的判断暂时先改成 0==0,也就是不判断用户,直接sudo 执行。

Reference

https://github.com/iqiancheng/fast-profiler

https://github.com/oldratlee/useful-scripts

Comments
Write a Comment
  • Hi 楼主

    > 如果遇到说用户不匹配的问题,这个是由于脚本本身的bug造成的,于是我fix了这个bug

    这个不是Bug哦,而 系统可靠性的考虑,避免系统自动运行时因为要输入root密码卡死操作。

    所以 才加上 上面好几行实现代码。用户问题提示时,会给出给出重新运行说明和命令行。

    更多说明,参见 PR #50中的讨论:

    https://github.com/oldratlee/useful-scripts/pull/50#issuecomment-156785326

    • @oldratlee 多谢原作者指出,看了PR,最新的功能又强大了! 大家也可以去看看 https://github.com/oldratlee/useful-scripts

  • > 说明当前用户下的sudo which jstack 找不到在usr/sbin下的jstack命令,可以通过下面的命令修复

    > $ sudo ln -s /usr/java/jdk1.8.0_101/bin/jstack /usr/sbin/jstack

    jstack命令会自动从 PATH 和 JAVA_HOME上去查找。

    也可以通过 -s 选项显式指定: show-busy-java-threads -s path/to/jstack

    对于sudo方式的运行,JAVA_HOME环境变量不能传递给root,而root用户往往没有配置JAVA_HOME且不方便配置,显式指定jstack命令的路径就反而显得更方便了。

    更全的用法参见文档:

    https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#%E7%94%A8%E6%B3%95

  • 最近 终于支持使用 top获取 cpu占用率 😂 ,top这个问题 放了5年!😤

    更多参见:

    https://github.com/oldratlee/useful-scripts/issues/67

    https://github.com/oldratlee/useful-scripts/issues/23