1 JDK常用命令
1.1 jsp命令
jps虚拟机进程状态工具。具体的执行结果如下:
jps命令格式:
jps [options] [hostid]
jps的其他常用选项
命令格式中,如果需要查看远端机器上的进程,则需要填写对应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
执行结果如下:
每一列的说明如下:
jstat常见选项:
在查看下具体的JIT编译情况:
上面的各列表示:编译数量,失败数量,不可用数量,耗时,失败类型,失败方法
查看类装载情况:
上面的各列表示:加载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情况:
其中1099端口为默认端口
1.3 jinfo
jinfo的作用是实时查看和调整虚拟机各项参数配置。命令基本格式:
jinfo [option] pid
仅支持本地的进程修改。基本上的格式为
jinfo -flag [+|-] name jinfo -flag name = value
jinfo还支持打印当前的System.getProperties()中的信息,具体的如下:
1.4 jmap
jmap是堆转存储快照的命令。当然也可以不用这个,可以在应用启动时增加-XX:+HeapDumpOnOutOfMemoryError参数。jmap命令格式
jmap [option] vmid
option的主要选项
具体的截图如下:
1.5 jhat
jhat命令是和jmap命令搭配使用的,主要使用来分析堆存储快照的。jhat命令太过简陋,针对上面生成的dump文件,分析如下:
打开浏览器访问7000端口:
1.6 jstack
jstack命令用于生成虚拟机当前时刻线程快照。线程快照是当前虚拟机内每一条线程正在执行的方法堆栈的集合。主要用于定位线程长时间停顿(线程死锁,死循环,寻求外部资源等)。jstack名称格式为:
jstack [option] vmid
参数主要选项:
本地编写一个循环,每输出一段日志后就休眠1s.对应的堆栈信息如下:
可以看到截图中提示无死锁
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.
可以看到,在最后有一个死锁。
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:
同样的,选择本地进程,通过JConsole来查看是否有死锁:
选择好线上后,点击检查死锁,就会发现有相应的提示如:
JConsole可以观察内存,线程,类加载数等各个维度的数据.
2.2 VisualVM
VisualVM是目前为止JDK发布的功能最强大的运行监控和故障处理工具。VisualVM可以做到:
1 显示虚拟机进程以及进程的配置,环境信息 2 监视应用程序的CPU,GC,堆,方法区以及线程的信息 3 dump和分析堆快照 4 方法级的程序运行性能分析
启动VisualVM: 执行jvisualvm(mac下)
VisualVM可以根据需要安装不同的插件,每个插件的关注点都不同,有的主要监控GC,有的主要监控内存,有的监控线程等。选择工具 -> 插件:
继续针对上面的死锁来通过VisualVM进行检查:
通过点击线下,找到对应的thread2后,提示检查到死锁。
这里只是简单的介绍,后面针对性能调优时会有介绍使用