這篇文章主要來介紹下 JDK 內建的命令,話不多說,讓我們開始吧!
javap
使用 javap
可以檢視 Java 位元組碼反編譯的原始檔,javap
的命令格式如下:
下面來演示下用 javap -c
對程式碼進行反編譯,首先寫個 HelloWorld
類,如下:
public class HelloWorld {
public static void main(String []args) {
System.out.println("Hello World");
}
}
接著使用 javap -c HelloWorld.class
就可以反編譯得到如下結果:
Compiled from "HelloWorld.java"
public class HelloWorld {
public HelloWorld();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello World
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
jps
jps
是用來查詢當前所有程式 pid 的,命令的用法如下圖所示:
執行 jps
可以獲取本機 Java 程式的 pid,執行結果如下:
[root@wupx ~]# jps
8825 spring-boot-0.0.1-SNAPSHOT.jar
使用 jps -mlvV
可以獲取到這個程式的 pid、jar 包的名字以及 JVM 引數等。
[root@wupx ~]# jps -mlvV
8825 /root/spring-boot-0.0.1-SNAPSHOT.jar --server.port=8090 --logging.file=/root/log/spring-boot.log -Xmx1024m -Xms1024m
jstat
jstat
主要用於監控 JVM,主要是 GC 資訊,在效能優化的時候經常用到,命令內容如下所示:
比如,上面我們通過 jps
查到的程式號 8825,我們使用 jstat -gc 8825
來檢視該程式的 GC 資訊:
[root@wupx ~]# jstat -gc 8825
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
65536.0 69120.0 0.0 160.0 10425344.0 1036247.8 21135360.0 19489859.7 84608.0 81123.8 9600.0 8834.1 99517 2070.459 0 0.000 2070.459
其中 S0C
表示當前 Survivor0
的容量,S1C
表示當前 Survivor1
的容量,S0U
表示當前 Survivor0
的利用率,S1U
表示當前 Survivor1
的利用率,EC
表示 Eden 的容量,EU
表示 Eden 的利用率,OC
表示老年代的容量,OU
表示老年代的利用率,MC
表示 Metaspace 的容量,MU
表示 Metaspace 的利用率,CCSC
表示類指標壓縮空間容量,CCSU
表示使用的類指標壓縮空間,YGC
表示新生代 GC 的次數,YGCT
表示新生代 GC 的時間,FGC
表示 Full Gc 的次數,FGCT
表示 Full GC 的時間,GCT
表示 GC 總時間。
每個物件都有一個指向它自身類的指標,_klass: 指向類的 4 位元組指標,64 位平臺上 _klass: 指向類的 8 位元組的指標,為了節約這些空間,引入了類指標壓縮空間。
jcmd
jcmd
可以檢視 JVM 資訊,常用的命令內容如下:
先使用 jcmd 8825 help
來檢視都支援什麼命令:
[root@wupx ~]# jcmd 8825 help
8825:
The following commands are available:
JFR.stop
JFR.start
JFR.dump
JFR.check
VM.native_memory
VM.check_commercial_features
VM.unlock_commercial_features
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
VM.classloader_stats
GC.rotate_log
Thread.print
GC.class_stats
GC.class_histogram
GC.heap_dump
GC.finalizer_info
GC.heap_info
GC.run_finalization
GC.run
VM.uptime
VM.dynlibs
VM.flags
VM.system_properties
VM.command_line
VM.version
help
下面我就選一個引數給大家舉個例子,比如列印堆的資訊,使用 jcmd 8825 GC.heap_dump
命令:
[root@wupx ~]# jcmd 8825 GC.heap_info
8825:
PSYoungGen total 628736K, used 41772K [0x0000000715a00000, 0x0000000746480000, 0x00000007c0000000)
eden space 609792K, 4% used [0x0000000715a00000,0x00000007173d5478,0x000000073ad80000)
from space 18944K, 80% used [0x000000073ad80000,0x000000073bc75e68,0x000000073c000000)
to space 19968K, 0% used [0x0000000745100000,0x0000000745100000,0x0000000746480000)
ParOldGen total 250880K, used 21756K [0x00000005c0e00000, 0x00000005d0300000, 0x0000000715a00000)
object space 250880K, 8% used [0x00000005c0e00000,0x00000005c233f160,0x00000005d0300000)
Metaspace used 44797K, capacity 45562K, committed 45824K, reserved 1089536K
class space used 5669K, capacity 5832K, committed 5888K, reserved 1048576K
可以看出可以獲取新生代、老年代、元空間、Eden、From Survivor 以及 To Survivor 的大小和佔比。
jmap
jmap
列印出 Java 程式記憶體中 Object 的情況,或者將 JVM 中的堆以二進位制輸出成文字,命令內容如下:
使用 jmap -heap 8825
檢視當前堆的使用資訊:
[root@wupx ~]# jmap -heap 8825
Attaching to process ID 8825, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.201-b09
using thread-local object allocation.
Parallel GC with 10 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 8575254528 (8178.0MB)
NewSize = 178782208 (170.5MB)
MaxNewSize = 2858418176 (2726.0MB)
OldSize = 358088704 (341.5MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 624427008 (595.5MB)
used = 32083672 (30.597373962402344MB)
free = 592343336 (564.9026260375977MB)
5.138098062536078% used
From Space:
capacity = 19398656 (18.5MB)
used = 15687272 (14.960548400878906MB)
free = 3711384 (3.5394515991210938MB)
80.86782919394004% used
To Space:
capacity = 20447232 (19.5MB)
used = 0 (0.0MB)
free = 20447232 (19.5MB)
0.0% used
PS Old Generation
capacity = 256901120 (245.0MB)
used = 22278496 (21.246429443359375MB)
free = 234622624 (223.75357055664062MB)
8.672012017697703% used
24741 interned Strings occupying 2987512 bytes.
首先會列印堆的一些相關配置,比如最大新生代、元空間的大小等;下面為堆的使用情況,包括新生代的 Eden 區、S0 區、S1 區以及老年代。
jmap
還可以將堆的資訊以檔案的形式儲存下來,相當於檔案快照,執行 jmap -dump:live,format=b,file=heap.bin 8825
命令:
[root@wupx ~]# jmap -dump:live,format=b,file=heap.bin 8825
Dumping heap to /root/heap.bin ...
Heap dump file created
這個 heap.bin
可以使用 jhat
命令開啟,是以 html 的形式展示的。
jhat
jhat
分析 Java 堆的命令,可以將堆中物件以 html
的形式顯示出來,支援物件查詢語言 OQL,命令內容如下:
現在執行 jhat -port 9999 heap.bin
來將剛剛儲存的 heap.bin
以 html 展示出來:
[root@wupx ~]# jhat -port 9999 heap.bin
Reading from heap.bin...
Dump file created Tue May 12 22:31:55 CST 2020
Snapshot read, resolving...
Resolving 570997 objects...
Chasing references, expect 114 dots..................................................................................................................
Eliminating duplicate references..................................................................................................................
Snapshot resolved.
Started HTTP server on port 9999
Server is ready.
執行完畢後,開啟 http://localhost:9999/
就可以看到類的例項的堆佔用情況,它是按照包名來分組的:
網頁的底部還有許多 Query 方式:
下面以 OQL 為例,開啟後是一個類似 SQL 查詢的視窗,比如輸入 select s from java.lang.String s where s.value.length >= 100
就可以查詢字串長度大於 100 的例項:
jstack
jstack
是堆疊跟蹤工具,主要用於列印給定程式 pid 的堆疊資訊,一般在發生死鎖或者 CPU 100% 的時候排查問題使用,可以去查詢當前執行的執行緒以及執行緒的堆疊資訊是什麼情況,命令內容如下:
下面執行 jstack -F 8825 > jstack.log
命令,將執行緒的資訊儲存下來:
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.161-b12 mixed mode):
"Attach Listener" #51805777 daemon prio=9 os_prio=0 tid=0x00007f971c001000 nid=0x9cd6 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"DestroyJavaVM" #55 prio=5 os_prio=0 tid=0x00007f9fc8009800 nid=0x227a waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"http-nio-8111-Acceptor-0" #52 daemon prio=5 os_prio=0 tid=0x00007f96c40c5800 nid=0x2653 runnable [0x00007f97c0df9000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)
- locked <0x00007f982c6213c8> (a java.lang.Object)
at org.apache.tomcat.util.net.NioEndpoint$Acceptor.run(NioEndpoint.java:455)
at java.lang.Thread.run(Thread.java:748)
"http-nio-8111-ClientPoller-0" #50 daemon prio=5 os_prio=0 tid=0x00007f9fc8e7e000 nid=0x2651 runnable [0x00007f97c21fb000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
- locked <0x00007f982c622460> (a sun.nio.ch.Util$3)
- locked <0x00007f982c622450> (a java.util.Collections$UnmodifiableSet)
- locked <0x00007f982c622408> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:787)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #17 daemon prio=9 os_prio=0 tid=0x00007f9fc8379000 nid=0x229d runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread10" #15 daemon prio=9 os_prio=0 tid=0x00007f9fc8373800 nid=0x229b waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"VM Thread" os_prio=0 tid=0x00007f9fc831c000 nid=0x228d runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f9fc801e800 nid=0x227b runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f9fc8020800 nid=0x227c runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007f9fc837e000 nid=0x229e waiting on condition
JNI global references: 357
因為內容比較多,擷取了部分內容,可以看出會列印出執行緒的資訊、狀態以及堆疊,也會列印出 GC Task 的執行緒資訊(ParallelGC 屬於並行收集器,預設為 2 個執行緒),從中可以分析出每個執行緒都在做什麼,如果伺服器 CPU 佔用高,可以看有多少個執行緒處於 RUNNABLE 狀態,一般是由於處於 RUNNABLE 狀態的執行緒過多,導致 CPU 過高;如果很多執行緒處於 TIMED_WAITING 狀態,理論上 CPU 佔用不會很高。
總結
本文主要對 JDK 常用的內建命令 javap、jps、jstat、jcmd、jmap、jhat、jstack
進行了簡單講解,大家可以自己在本機進行實踐。
瞭解這些命令後會在死鎖、CPU 佔用過高問題的排查、程式效能調優上會有很大的幫助,以後還會介紹 JDK 自帶的圖形化工具以及 CPU 佔用過高的排查例項。
最好的關係就是互相成就,大家的在看、轉發、留言三連就是我創作的最大動力。
參考
https://docs.oracle.com/javase/8/docs/technotes/tools/index.html#basic
JDK 內建命令工具