1 JDK常用命令
1.1 jsp命令
jps虛擬機進程狀態(tài)工具。具體的執(zhí)行結果如下:
jps命令格式:
jps [options] [hostid]
jps的其他常用選項
命令格式中,如果需要查看遠端機器上的進程,則需要填寫對應hostid就好。具體的如下:
jps 10.**.**.148
1.2 jstat
用于監(jiān)視虛擬機各種運行狀態(tài)信息的命令行工具,它可以顯示本地或者是遠程虛擬機進程中的類裝載,內(nèi)存,垃圾收集,JIT編譯等運行數(shù)據(jù)。jstat命令格式為:
jstat [ option vmid [ interval[s|ms] [count]]
其中vmid如果是本地進程,則vmid對應的為虛擬機進程號。如果是遠程虛擬機進程,則對應的格式應該是:
[protocol:][//] vmid[@hostname[:port]/servername]
遠程機器上的服務進程需要支持RMI,使用jstad工具可以很方便的簡歷RMI服務器。
參數(shù)interval和count表示間隔和次數(shù)。例如針對本地的進程查看其每隔一段時間對應的GC情況:
jstat -gc 3836 1000 20
執(zhí)行結果如下:
每一列的說明如下:
jstat常見選項:
在查看下具體的JIT編譯情況:
上面的各列表示:編譯數(shù)量,失敗數(shù)量,不可用數(shù)量,耗時,失敗類型,失敗方法
查看類裝載情況:
上面的各列表示:加載class的數(shù)量,加載字節(jié)數(shù),未加載類數(shù)量,未加載占用空間,耗時
通過開始的jstat命令格式可以知道,jstat支持遠程監(jiān)控,具體的監(jiān)控需要依賴于jstatd命令。jstatd命令需要現(xiàn)在遠程機器上開啟一個rmi進程。用于支持遠端連接。
在任意目錄下創(chuàng)建一個.policy文件:jstatd.all.policy
grant codebase "file:${JAVA.home}/../lib/tools.jar" { permission java.security.AllPermission; };
然后在下執(zhí)行(這里是當前目錄下直接執(zhí)行的,java.security.policy執(zhí)行你的文件所在的目錄地址也是可以的):
jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=10.**.**.148
在本地進行調(diào)用,查看遠端上某一進程的GC情況:
其中1099端口為默認端口
1.3 jinfo
jinfo的作用是實時查看和調(diào)整虛擬機各項參數(shù)配置。命令基本格式:
jinfo [option] pid
僅支持本地的進程修改。基本上的格式為
jinfo -flag [+|-] name jinfo -flag name = value
jinfo還支持打印當前的System.getProperties()中的信息,具體的如下:
1.4 jmap
jmap是堆轉(zhuǎn)存儲快照的命令。當然也可以不用這個,可以在應用啟動時增加-XX:+HeapDumpOnOutOfMemoryError參數(shù)。jmap命令格式
jmap [option] vmid
option的主要選項
具體的截圖如下:
1.5 jhat
jhat命令是和jmap命令搭配使用的,主要使用來分析堆存儲快照的。jhat命令太過簡陋,針對上面生成的dump文件,分析如下:
打開瀏覽器訪問7000端口:
1.6 jstack
jstack命令用于生成虛擬機當前時刻線程快照。線程快照是當前虛擬機內(nèi)每一條線程正在執(zhí)行的方法堆棧的集合。主要用于定位線程長時間停頓(線程死鎖,死循環(huán),尋求外部資源等)。jstack名稱格式為:
jstack [option] vmid
參數(shù)主要選項:
本地編寫一個循環(huán),每輸出一段日志后就休眠1s.對應的堆棧信息如下:
可以看到截圖中提示無死鎖
No deadlocks found.
截圖中并無死鎖。所有的線程基本都是Blocked。那么應該是線程都屬于休眠狀態(tài)。具體的看:
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線程啟動后,先持有l(wèi)ock2,在休眠1ms后,申請lock1。這個時候lock1已經(jīng)被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對象出現(xiàn)死鎖。
2 JDK可視化工具
2.1 JConsole
jconsole是一種基于JMX的可視化監(jiān)視,管理工具。它管理部分的功能是針對JMX MBean進行管理。啟動JConsole:
同樣的,選擇本地進程,通過JConsole來查看是否有死鎖:
選擇好線上后,點擊檢查死鎖,就會發(fā)現(xiàn)有相應的提示如:
JConsole可以觀察內(nèi)存,線程,類加載數(shù)等各個維度的數(shù)據(jù).
2.2 VisualVM
VisualVM是目前為止JDK發(fā)布的功能最強大的運行監(jiān)控和故障處理工具。VisualVM可以做到:
1 顯示虛擬機進程以及進程的配置,環(huán)境信息 2 監(jiān)視應用程序的CPU,GC,堆,方法區(qū)以及線程的信息 3 dump和分析堆快照 4 方法級的程序運行性能分析
啟動VisualVM: 執(zhí)行jvisualvm(mac下)
VisualVM可以根據(jù)需要安裝不同的插件,每個插件的關注點都不同,有的主要監(jiān)控GC,有的主要監(jiān)控內(nèi)存,有的監(jiān)控線程等。選擇工具 -> 插件:
繼續(xù)針對上面的死鎖來通過VisualVM進行檢查:
通過點擊線下,找到對應的thread2后,提示檢查到死鎖。
這里只是簡單的介紹,后面針對性能調(diào)優(yōu)時會有介紹使用