查看原文
其他

推荐:排查Java应用线上问题工具汇总。

作者:Byte_Liu

来源:http://byteliu.com


# jvm自带的工具


Java虚拟机自带了很多工具,其中就有我们比较熟悉的javac和java,除此之外,我们还得掌握一类用于线上问题排查的工具,比如jstack, jmap, jstat等,通过这些工具我们可以深入了解JVM当下正在干什么事。


1、JPS


JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程(当然,Linux中我们也可以直接ps -ef | grep 'java'查询)。


命令格式:

jps [options] [hostid]
option参数-l : 输出主类全名或jar路径-q : 仅输出VM标识符(即java进程id),不包括classname,jar name,arguments in main method-m : 输出JVM启动时传递给main()的参数(arguments in main method)-v : 输出JVM启动时显示指定的JVM参数-V : 输出通过flag文件传递到JVM中的参数(.hotspotrc文件或-XX:Flags=所指定的文件
hostid参数hostid也叫做host identifier,当用户查询远程机器上的所有的Java虚拟机进程时就需要传递该参数(注意:如果需要查看其他机器上的jvm进程,需要在待查看机器上启动jstatd。)。该参数格式类似URL。参数格式:[protocol:][[//]hostname][:port][/servername]
其中[option]、[hostid]参数均为非必填。


2、JSTACK


通过名称就可以看出这是一个查看栈信息的工具,我们知道栈是线程私有的(栈也叫线程栈),所以查看栈信息就是查看线程的信息。

jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到 当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

在用top -H看到占用CPU非常高的pid时,先把pid转换成16进制(使用printf "%x\n" pid即可得到pid的16进制值 ),然后再在jstack工具dump出来的信息中搜索,就可以知道到底是哪个线程占用了较高的CPU资源。

命令格式:

jstack [options] pid
option参数-F : 当正常输出请求不被响应时,强制输出线程堆栈-l : 除堆栈外,显示关于锁的附加信息-m : 如果调用到本地方法的话,可以显示C/C++的堆栈-h : 打印帮助信息

jstack dump出来的线程有几种状态:

1.Runnable
线程具备所有运行条件,在运行队列中准备操作系统的调度,或者正在运行。


2.Wait on condition
线程等待某个条件的发生,比如IO、sleep到时


3.Waiting for monitor entry
等待进入临界区


4.in Object.wait()
临界区中调用了wait方法,等待其他线程调用notify或者notifyAll唤醒


运行下面这个例子:

public class JstackThreadStatusDemo {
public static void main(String args[]){ Object lock = new Object(); new Thread(new TaskOne(lock),"taskOne_1").start(); new Thread(new TaskOne(lock),"taskOne_2").start();
Object lock2 = new Object(); new Thread(new TaskTwo(lock2),"taskTwo_1").start();
while(true){
} }
static class TaskOne implements Runnable{ Object lock;
public TaskOne(Object lock) { this.lock = lock; }
public void run() { synchronized (lock){ while(true){ System.out.println("****"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
static class TaskTwo implements Runnable{ Object lock;
public TaskTwo(Object lock) { this.lock = lock; }
public void run() { synchronized (lock){ while(true){ System.out.println(Thread.currentThread().getName()+" enter monitor"); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); }
}
} } }}

然后用jps命令找到这个进程的pid,运行jstack命令dump出线程信息如下:

Last login: Sat Jan 26 21:56:58 on ttys016ali-6c96cfd9fc63:~ liuxiao$ jps42210 Launcher42211 JstackThreadStatusDemo68373 Launcher39940 Application8349342247 Jps46103 Launcher6362483513 Launcher46104 Applicationali-6c96cfd9fc63:~ liuxiao$ jstack -l 422112019-01-26 22:16:25Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.151-b12 mixed mode):
"Attach Listener" #14 daemon prio=9 os_prio=31 tid=0x00007fc38f827000 nid=0x1207 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"taskTwo_1" #13 prio=5 os_prio=31 tid=0x00007fc38e043800 nid=0xa803 in Object.wait() [0x0000700002ef9000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076adb1d10> (a java.lang.Object) at java.lang.Object.wait(Object.java:502) at com.byteliu.demo.JstackThreadStatusDemo$TaskTwo.run(JstackThreadStatusDemo.java:64) - locked <0x000000076adb1d10> (a java.lang.Object) at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers: - None
"taskOne_2" #12 prio=5 os_prio=31 tid=0x00007fc38e840000 nid=0x5903 waiting for monitor entry [0x0000700002df6000] java.lang.Thread.State: BLOCKED (on object monitor) at com.byteliu.demo.JstackThreadStatusDemo$TaskOne.run(JstackThreadStatusDemo.java:40) - waiting to lock <0x000000076adad118> (a java.lang.Object) at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers: - None
"taskOne_1" #11 prio=5 os_prio=31 tid=0x00007fc38c832800 nid=0x5703 waiting on condition [0x0000700002cf3000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at com.byteliu.demo.JstackThreadStatusDemo$TaskOne.run(JstackThreadStatusDemo.java:42) - locked <0x000000076adad118> (a java.lang.Object) at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers: - None
"Service Thread" #10 daemon prio=9 os_prio=31 tid=0x00007fc38e032800 nid=0x3f03 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"C1 CompilerThread3" #9 daemon prio=9 os_prio=31 tid=0x00007fc38c806000 nid=0x3d03 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"C2 CompilerThread2" #8 daemon prio=9 os_prio=31 tid=0x00007fc38c805800 nid=0x4103 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"C2 CompilerThread1" #7 daemon prio=9 os_prio=31 tid=0x00007fc38e007800 nid=0x4303 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"C2 CompilerThread0" #6 daemon prio=9 os_prio=31 tid=0x00007fc38f80a800 nid=0x3b03 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"Monitor Ctrl-Break" #5 daemon prio=5 os_prio=31 tid=0x00007fc38b82a000 nid=0x4603 runnable [0x00007000025de000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) - locked <0x000000076ac82fe0> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:161) at java.io.BufferedReader.readLine(BufferedReader.java:324) - locked <0x000000076ac82fe0> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:389) at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
Locked ownable synchronizers: - None
"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fc38e81c000 nid=0x4703 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fc38e005000 nid=0x4d03 in Object.wait() [0x00007000023d8000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076ab08ec8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143) - locked <0x000000076ab08ec8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
Locked ownable synchronizers: - None
"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fc38c00d000 nid=0x4f03 in Object.wait() [0x00007000022d5000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076ab06b68> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x000000076ab06b68> (a java.lang.ref.Reference$Lock) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
Locked ownable synchronizers: - None
"main" #1 prio=5 os_prio=31 tid=0x00007fc38f001800 nid=0x2503 runnable [0x00007000018b7000] java.lang.Thread.State: RUNNABLE at com.byteliu.demo.JstackThreadStatusDemo.main(JstackThreadStatusDemo.java:25)
Locked ownable synchronizers: - None
"VM Thread" os_prio=31 tid=0x00007fc38b81f000 nid=0x5003 runnable
"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fc38f800800 nid=0x1d07 runnable
"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fc38f801800 nid=0x2a03 runnable
"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fc38f802000 nid=0x2c03 runnable
"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fc38f802800 nid=0x2e03 runnable
"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007fc38b809800 nid=0x5303 runnable
"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007fc38b80a800 nid=0x5203 runnable
"GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007fc38f803000 nid=0x3103 runnable
"GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007fc38b80b000 nid=0x3203 runnable
"VM Periodic Task Thread" os_prio=31 tid=0x00007fc38b862000 nid=0x5503 waiting on condition
JNI global references: 30
ali-6c96cfd9fc63:~ liuxiao$

我们可以看到不同的线程处于不同的


3、JSTAT


jstat(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

命令格式

jstat [option] pid [interval] [count]参数[option] : 操作参数pid : 本地虚拟机进程ID[interval] : 连续输出的时间间隔[count] : 连续输出的次数

option 参数总览


4、JMAP


jmap(JVM Memory Map)命令用于生成heap dump文件。heap dump文件也可以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候自动生成dump文件。jmap不仅能生成dump文件,还可以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。排查GC问题必然会用到的工具,jmap可以告诉你当前JVM内存堆中的对象分布及其关系,当dump堆之后可以用MAT分析,看看有哪些大对象,或者哪些类的实例特别多。

命令格式

jmap [option] pid参数[option] : 操作参数pid : 本地虚拟机进程ID
option参数-dump : 生成堆转储快照,jmap -dump:live,format=b,file=<filename> -pid-finalizerinfo : 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象-heap : 显示Java堆详细信息-histo : 显示堆中对象的统计信息,jmap -histo:live pid 如果带上live则只统计活对象,因此不加live的堆大小要大于加live堆的大小 )-permstat : to print permanent generation statistics-F : 当-dump没有响应时,强制生成dump快照


5、JHAT


jhat(JVM Heap Analysis Tool)命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后, 可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制 到本地或其他机器上进行分析。

命令格式

jhat [dumpfile]


6、JINFO


jinfo(JVM Configuration info)这个命令作用是实时查看和调整虚拟机运行参数。之前的jps -v命令只能查看到显示指定的参数,如果想要查看未被显示指定的参数的值就要使用jinfo命令。


命令格式

jinfo [option] [args] pidoption参数-flag : 输出指定args参数的值-flags : 不需要args参数,输出所有JVM参数的值-sysprops : 输出系统属性,等同于System.getProperties()


7、JCMD


在JDK1.7以后,新增了一个命令行工具 jcmd。他是一个多功能的工具,可以用它来导出堆、查看Java进程、导出线程信息、执行GC、还可以进行采样分析(jmc 工具的飞行记录器)。

命令格式

jcmd <pid | main class> <command ... | PerfCounter.print | -f file> jcmd -l jcmd -h
描述pid:接收诊断命令请求的进程ID。main class :接收诊断命令请求的进程的main类。匹配进程时,main类名称中包含指定子字符串的任何进程均是匹配的。如果多个正在运行的Java进程共享同一个main类,诊断命令请求将会发送到所有的这些进程中。
command:接收诊断命令请求的进程的main类。匹配进程时,main类名称中包含指定子字符串的任何进程均是匹配的。如果多个正在运行的Java进程共享同一个main类,诊断命令请求将会发送到所有的这些进程中。注意: 如果任何参数含有空格,你必须使用英文的单引号或双引号将其包围起来。此外,你必须使用转义字符来转移参数中的单引号或双引号,以阻止操作系统shell处理这些引用标记。当然,你也可以在参数两侧加上单引号,然后在参数内使用双引号(或者,在参数两侧加上双引号,在参数中使用单引号)。
Perfcounter.print:打印目标Java进程上可用的性能计数器。性能计数器的列表可能会随着Java进程的不同而产生变化。-f file:从文件file中读取命令,然后在目标Java进程上调用这些命令。在file中,每个命令必须写在单独的一行。以"#"开头的行会被忽略。当所有行的命令被调用完毕后,或者读取到含有stop关键字的命令,将会终止对file的处理。-l:查看所有的进程列表信息。-h:查看帮助信息。(同 -help)


# 其他工具


1、TOP


linux自带的命令,查看系统资源消耗情况,可以看看CPU、内存、SWAP、I/O的消耗情况,需要特别注意的有几个值:
1.ni,这个值如果特别高说明线程上下文切换开销较大,看看是不是开了太多的线程导致的
2.res,这个代表了进程实际占用的内存
3.swap,内存不足就会占用swap空间,这个时候一般应用的性能会急剧下降,需要特别关注

2、MAT( ECLIPSE MEMORY ANALYZER)


用于分析jmap或OOM时dump出来的内存快照,可以看到对象和引用关系。
官方地址: http://www.eclipse.org/mat/

3、BTRACE


Btrace是一个实时监控工具,不需要修改应用代码,采用修改字节码的方式来进行监控,是性能调优和线上问题诊断的利器。

它可以获取应用程序代码的执行时间,可以让你无需修改代码,帮你做时间的打点。
我们只需要编写btrace脚本,它是一个java文件。

github地址:
https://github.com/btraceio/btrace

4、HOUSEMD


作者是阿里的聚石。类似于BTrace,是一款非常敏捷的Java进程运行时的诊断调式命令行工具,用于对JVM运行时的状态进行追踪和诊断。HouseM取名同名美剧“豪斯医生”,它将常用的几个Btrace脚本整合在一起形成一个独立风格的应用,具备安全易用高效的特点,其核心代码用的是Scala。目前已经停止更新。

通常我们排查问题很多时候都在代码中加个日志,看看方法的参数、返回值是不是我们期望的,然后编译打包部署重启应用,十几分钟就过去了。HouseMD可以直接让你可以追踪到方法的返回值和参数,以及调用次数、调用平均rt、调用栈。甚至是类的成员变量的值、Class加载的路径、对应的ClassLoader,都可以用一行命令给你展现出来。

github地址:
 
https://github.com/CSUG/HouseMD

5、GREYS


作者是阿里的杜琨。Greys是一个JVM进程执行过程中的异常诊断工具,可以在不中断程序执行的情况下轻松完成问题排查工作。目前Greys仅支持Linux/Unix/Mac上的Java6+,Windows暂时无法支持.

和HouseMD一样,Greys-Anatomy取名同名美剧“实习医生格蕾”,目的是向前辈致敬。
代码编写的时候参考了BTrace和HouseMD两个前辈的思路。

github地址:
https://github.com/oldmanpushcart/greys-anatomy

6、TBJMAP


作者是阿里的叔同。通过jmap和MAT我们可以知道整个JVM堆的对象分布情况,但是有时候我们需要知道young/old/perm区分别有哪些对象的时候,就要用到TBJMap这个神器了。它可以告诉你各个分代区各个Class的实例数、占用的空间,以及DirectMemory占用的空间等。

github地址:
 https://github.com/alibaba/TBJMap

7、TSAR


作者是阿里的空见。tsar是淘宝的采集工具,主要用来收集服务器的系统信息(如cpu,io,mem,tcp等)以及应用数据(如squid haproxy nginx等),tsar支持t实时查看和历史查看,方便了解应用和服务器的信息。

用不同的参数可以看到历史和实时信息,CPU、Load、内存、网络、QPS、rt等等你想要的监控数据几乎都能看到。

github地址:https://github.com/alibaba/tsar

8、ARTHAS


Arthas是阿里巴巴最近开源出来的一个针对java问题诊断的工具,Arthas支持JDK 6+,支持Linux/Mac/Winodws,采用命令行交互模式,同时提供丰富的Tab自动补全功能,进一步方便进行问题的定位和诊断。

这个工具可以协助你做下面这些事情:
1.这个类是从哪个 jar 包加载而来的?
2.为什么会报各种类相关的 Exception?
3.线上遇到问题无法 debug 好蛋疼,难道只能反复通过增加 System.out 或通过加日志再重新发布吗?
4.线上的代码为什么没有执行到这里?是由于代码没有 commit?还是搞错了分支?
5.线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
6.是否有一个全局视角来查看系统的运行状况?
7.有什么办法可以监控到JVM的实时运行状态?

地址:https://github.com/alibaba/arthas

热文推荐

上班后咋防控?分享一份指南

场景分析:记录一下使用MySQL的left join时,遇到的坑!

经验:谷歌大佬告诉你,如何写一份面试官爱不释手的简历!



觉得不错,请给个「在看」

分享给你的朋友!


点我,查看更多精彩文章。

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存