推荐:排查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 ttys016
ali-6c96cfd9fc63:~ liuxiao$ jps
42210 Launcher
42211 JstackThreadStatusDemo
68373 Launcher
39940 Application
83493
42247 Jps
46103 Launcher
63624
83513 Launcher
46104 Application
ali-6c96cfd9fc63:~ liuxiao$ jstack -l 42211
2019-01-26 22:16:25
Full 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] pid
option参数
-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
2、MAT( ECLIPSE MEMORY ANALYZER)
官方地址: http://www.eclipse.org/mat/
3、BTRACE
它可以获取应用程序代码的执行时间,可以让你无需修改代码,帮你做时间的打点。我们只需要编写btrace脚本,它是一个java文件。
github地址:
4、HOUSEMD
github地址:
5、GREYS
和HouseMD一样,Greys-Anatomy取名同名美剧“实习医生格蕾”,目的是向前辈致敬。代码编写的时候参考了BTrace和HouseMD两个前辈的思路。
github地址:
6、TBJMAP
7、TSAR
用不同的参数可以看到历史和实时信息,CPU、Load、内存、网络、QPS、rt等等你想要的监控数据几乎都能看到。
8、ARTHAS
场景分析:记录一下使用MySQL的left join时,遇到的坑!
觉得不错,请给个「在看」
分享给你的朋友!