Java 故障分析处理工具

212人浏览 / 0人评论

Java 故障分析处理工具

1 JDK常用命令

1.1 jsp命令

jps虚拟机进程状态工具。具体的执行结果如下:

Java 故障处理工具

 

jps命令格式:

jps [options] [hostid]

jps的其他常用选项

Java 故障处理工具

 

命令格式中,如果需要查看远端机器上的进程,则需要填写对应hostid就好。具体的如下:

jps 10.**.**.148

1.2 jstat

用于监视虚拟机各种运行状态信息的命令行工具,它可以显示本地或者是远程虚拟机进程中的类装载,内存,垃圾收集,JIT编译等运行数据。jstat命令格式为:

jstat [ option vmid [ interval[s|ms] [count]]

其中vmid如果是本地进程,则vmid对应的为虚拟机进程号。如果是远程虚拟机进程,则对应的格式应该是:

[protocol:][//] vmid[@hostname[:port]/servername]

远程机器上的服务进程需要支持RMI,使用jstad工具可以很方便的简历RMI服务器。

参数interval和count表示间隔和次数。例如针对本地的进程查看其每隔一段时间对应的GC情况:

jstat -gc 3836 1000 20

执行结果如下:

Java 故障处理工具

 

每一列的说明如下:

Java 故障处理工具

 

jstat常见选项:

Java 故障处理工具

 

在查看下具体的JIT编译情况:

Java 故障处理工具

 

上面的各列表示:编译数量,失败数量,不可用数量,耗时,失败类型,失败方法

查看类装载情况:

Java 故障处理工具

 

上面的各列表示:加载class的数量,加载字节数,未加载类数量,未加载占用空间,耗时

通过开始的jstat命令格式可以知道,jstat支持远程监控,具体的监控需要依赖于jstatd命令。jstatd命令需要现在远程机器上开启一个rmi进程。用于支持远端连接。

在任意目录下创建一个.policy文件:jstatd.all.policy

grant codebase "file:${java.home}/../lib/tools.jar" {
 permission java.security.AllPermission;
};

然后在下执行(这里是当前目录下直接执行的,java.security.policy执行你的文件所在的目录地址也是可以的):

jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=10.**.**.148

在本地进行调用,查看远端上某一进程的GC情况:

Java 故障处理工具

 

其中1099端口为默认端口

1.3 jinfo

jinfo的作用是实时查看和调整虚拟机各项参数配置。命令基本格式:

jinfo [option] pid

仅支持本地的进程修改。基本上的格式为

jinfo -flag [+|-] name
jinfo -flag name = value

jinfo还支持打印当前的System.getProperties()中的信息,具体的如下:

Java 故障处理工具

 

1.4 jmap

jmap是堆转存储快照的命令。当然也可以不用这个,可以在应用启动时增加-XX:+HeapDumpOnOutOfMemoryError参数。jmap命令格式

jmap [option] vmid

option的主要选项

Java 故障处理工具

 

具体的截图如下:

Java 故障处理工具

 

1.5 jhat

jhat命令是和jmap命令搭配使用的,主要使用来分析堆存储快照的。jhat命令太过简陋,针对上面生成的dump文件,分析如下:

Java 故障处理工具

 

打开浏览器访问7000端口:

Java 故障处理工具

 

1.6 jstack

jstack命令用于生成虚拟机当前时刻线程快照。线程快照是当前虚拟机内每一条线程正在执行的方法堆栈的集合。主要用于定位线程长时间停顿(线程死锁,死循环,寻求外部资源等)。jstack名称格式为:

jstack [option] vmid

参数主要选项:

Java 故障处理工具

 

本地编写一个循环,每输出一段日志后就休眠1s.对应的堆栈信息如下:

Java 故障处理工具

 

可以看到截图中提示无死锁

No deadlocks found.

截图中并无死锁。所有的线程基本都是Blocked。那么应该是线程都属于休眠状态。具体的看:

Thread 6915: (state = BLOCKED)
 - java.lang.Thread.sleep(long) @bci=0 (Compiled frame; information may be imprecise)
 - com.jvm.study.test.TestMain.main(java.lang.String[]) @bci=22, line=10 (Interpreted frame)

有sleep操作。线程休眠等待唤醒。

编写一段死锁的代码,通过jstack来分析具体的问题:

public class TestMain {

 private static final Object lock1 = new Object();
 private static final Object lock2 = new Object();

 public static void main(String[] args) throws Exception{
 Thread t1 = new Thread(new Runnable() {
 public void run() {
 try {
 synchronized (lock1){
 System.out.println(Thread.currentThread().getName());
 TimeUnit.SECONDS.sleep(1);
 synchronized (lock2){
 System.out.println(Thread.currentThread().getName());
 }
 }
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 });
 Thread t2 = new Thread(new Runnable() {
 public void run() {
 try {
 synchronized (lock2){
 System.out.println(Thread.currentThread().getName());
 TimeUnit.SECONDS.sleep(1);
 synchronized (lock1){
 System.out.println(Thread.currentThread().getName());
 }
 }
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 });
 t1.setName("mythread1");
 t2.setName("mythread2");
 t1.start();
 t2.start();
 }
}

说明:在启动后,t1线程持有了lock1后,在休眠1ms后,在申请获取lock2。而t2线程启动后,先持有lock2,在休眠1ms后,申请lock1。这个时候lock1已经被t1持有。造成相互等待争夺的情况。线程死锁。通过jstack查看后如下:

2019-11-11 13:25:59
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode):

"Attach Listener" #13 daemon prio=9 os_prio=31 tid=0x00007f8182802800 nid=0x1107 waiting on condition [0x0000000000000000]
 java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #12 prio=5 os_prio=31 tid=0x00007f818206d000 nid=0x1b03 waiting on condition [0x0000000000000000]
 java.lang.Thread.State: RUNNABLE

"mythread2" #11 prio=5 os_prio=31 tid=0x00007f8182011000 nid=0x5103 waiting for monitor entry [0x000070000a6fb000]
 java.lang.Thread.State: BLOCKED (on object monitor)
 at com.jvm.study.test.TestMain$2.run(TestMain.java:33)
 - waiting to lock <0x00000007957843b0> (a java.lang.Object)
 - locked <0x00000007957843c0> (a java.lang.Object)
 at java.lang.Thread.run(Thread.java:748)

"mythread1" #10 prio=5 os_prio=31 tid=0x00007f8182064000 nid=0x4f03 waiting for monitor entry [0x000070000a5f8000]
 java.lang.Thread.State: BLOCKED (on object monitor)
 at com.jvm.study.test.TestMain$1.run(TestMain.java:18)
 - waiting to lock <0x00000007957843c0> (a java.lang.Object)
 - locked <0x00000007957843b0> (a java.lang.Object)
 at java.lang.Thread.run(Thread.java:748)

"Service Thread" #9 daemon prio=9 os_prio=31 tid=0x00007f81830e0000 nid=0x4b03 runnable [0x0000000000000000]
 java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #8 daemon prio=9 os_prio=31 tid=0x00007f81830d5000 nid=0x4903 waiting on condition [0x0000000000000000]
 java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #7 daemon prio=9 os_prio=31 tid=0x00007f81830d4000 nid=0x4703 waiting on condition [0x0000000000000000]
 java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #6 daemon prio=9 os_prio=31 tid=0x00007f8181883800 nid=0x4503 waiting on condition [0x0000000000000000]
 java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #5 daemon prio=5 os_prio=31 tid=0x00007f8181882000 nid=0x4303 runnable [0x0000700009fe6000]
 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 <0x0000000795708388> (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 <0x0000000795708388> (a java.io.InputStreamReader)
 at java.io.BufferedReader.readLine(BufferedReader.java:389)
 at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)

"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007f8182002800 nid=0x4103 runnable [0x0000000000000000]
 java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007f8183010000 nid=0x3003 in Object.wait() [0x0000700009d5d000]
 java.lang.Thread.State: WAITING (on object monitor)
 at java.lang.Object.wait(Native Method)
 - waiting on <0x0000000795588ec8> (a java.lang.ref.ReferenceQueue$Lock)
 at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
 - locked <0x0000000795588ec8> (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)

"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007f818300d800 nid=0x2e03 in Object.wait() [0x0000700009c5a000]
 java.lang.Thread.State: WAITING (on object monitor)
 at java.lang.Object.wait(Native Method)
 - waiting on <0x0000000795586b68> (a java.lang.ref.Reference$Lock)
 at java.lang.Object.wait(Object.java:502)
 at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
 - locked <0x0000000795586b68> (a java.lang.ref.Reference$Lock)
 at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=31 tid=0x00007f8181845800 nid=0x2c03 runnable

"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007f8183007000 nid=0x2403 runnable

"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007f8183007800 nid=0x2603 runnable

"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007f8183008000 nid=0x2803 runnable

"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007f8183009000 nid=0x2a03 runnable

"VM Periodic Task Thread" os_prio=31 tid=0x00007f818282a000 nid=0x4d03 waiting on condition

JNI global references: 33


Found one Java-level deadlock:
=============================
"mythread2":
 waiting to lock monitor 0x00007f81828196b8 (object 0x00000007957843b0, a java.lang.Object),
 which is held by "mythread1"
"mythread1":
 waiting to lock monitor 0x00007f818281c158 (object 0x00000007957843c0, a java.lang.Object),
 which is held by "mythread2"

Java stack information for the threads listed above:
===================================================
"mythread2":
 at com.jvm.study.test.TestMain$2.run(TestMain.java:33)
 - waiting to lock <0x00000007957843b0> (a java.lang.Object)
 - locked <0x00000007957843c0> (a java.lang.Object)
 at java.lang.Thread.run(Thread.java:748)
"mythread1":
 at com.jvm.study.test.TestMain$1.run(TestMain.java:18)
 - waiting to lock <0x00000007957843c0> (a java.lang.Object)
 - locked <0x00000007957843b0> (a java.lang.Object)
 at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

可以看到,在最后有一个死锁。

Java 故障处理工具

 

t2线程在等待

waiting to lock <0x00000007957843b0> (a java.lang.Object)

t2持有的锁是

locked <0x00000007957843c0> (a java.lang.Object)

t1线程在等待

waiting to lock <0x00000007957843c0> (a java.lang.Object)

t1持有的锁是

locked <0x00000007957843b0> (a java.lang.Object)

相互在等待对方持有的锁。通过上面可以找到死锁的原因,通过lock a java.lang.Object对象出现死锁。

2 JDK可视化工具

2.1 JConsole

jconsole是一种基于JMX的可视化监视,管理工具。它管理部分的功能是针对JMX MBean进行管理。启动JConsole:

Java 故障处理工具

 

同样的,选择本地进程,通过JConsole来查看是否有死锁:

Java 故障处理工具

 

选择好线上后,点击检查死锁,就会发现有相应的提示如:

Java 故障处理工具

 

JConsole可以观察内存,线程,类加载数等各个维度的数据.

2.2 VisualVM

VisualVM是目前为止JDK发布的功能最强大的运行监控和故障处理工具。VisualVM可以做到:

1 显示虚拟机进程以及进程的配置,环境信息
2 监视应用程序的CPU,GC,堆,方法区以及线程的信息
3 dump和分析堆快照
4 方法级的程序运行性能分析

启动VisualVM: 执行jvisualvm(mac下)

Java 故障处理工具

 

VisualVM可以根据需要安装不同的插件,每个插件的关注点都不同,有的主要监控GC,有的主要监控内存,有的监控线程等。选择工具 -> 插件:

Java 故障处理工具

 

继续针对上面的死锁来通过VisualVM进行检查:

Java 故障处理工具

 

通过点击线下,找到对应的thread2后,提示检查到死锁。

这里只是简单的介绍,后面针对性能调优时会有介绍使用

全部评论