JVM 指令集概覽:基礎與應用
在現代軟件開發中,Java 語言憑借其“一次編寫,到處運行”的理念成為了企業級應用的首選之一。這一理念的背后支撐技術正是 Java 虛擬機(JVM)。JVM 是一個抽象的計算機,它實現了 Java 編程語言的各種特性,并且能夠執行編譯后的字節碼文件。了解 JVM 的工作原理對于優化程序性能、調試代碼以及深入理解 Java 應用至關重要。
本文將簡要介紹 JVM 中常見的指令及其作用,旨在為開發者提供一個清晰的認識框架,幫助他們更好地掌握 JVM 指令集的基本概念及其實戰技巧。我們將從最基礎的加載和存儲指令開始,逐步探討控制轉移、方法調用等更復雜的操作符,最終帶領讀者深入了解 JVM 內部工作機制的一角。
詳解JVM常見指令
jinfo(查看配置信息)
查看Java應用程序配置參數或者JVM系統屬性,相關命令詳情我們可以使用-help或者man命令查看:
jinfo -help
對此我們也給出jinfo指令集的說明:
Usage:
jinfo [option] <pid>
(to connect to running process)
jinfo [option] <executable <core>
(to connect to a core file)
jinfo [option] [server_id@]<remote server IP or hostname>
(to connect to remote debug server)
where <option> is one of:
-flag <name> to print the value of the named VM flag
-flag [+|-]<name> to enable or disable the named VM flag
-flag <name>=<value> to set the named VM flag to the given value
-flags to print VM flags
-sysprops to print Java system properties
<no option> to print both of the above
-h | -help to print this help message
為了演示,筆者在服務器上開啟了一個Java應用,我們可以使用jps命令查看其進程id,可以看到筆者服務器中有一個pid為19946的Java進程,查看當前應用所有的配置參數以及系統配置屬性命令為jinfo pid,可以看到通過jinfo結合進程號即可看到這個java進程對應的各自類庫、版本號以及系統信息:
Attaching to process ID 19946, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.202-b08 Java System Properties: java.runtime.name = Java(TM) SE Runtime Environment java.vm.version = 25.202-b08 sun.boot.library.path = /root/jdk8/jre/lib/amd64 java.protocol.handler.pkgs = org.springframework.boot.loader java.vendor.url = http://java.oracle.com/ java.vm.vendor = Oracle Corporation path.separator = : file.encoding.pkg = sun.io java.vm.name = Java HotSpot(TM) 64-Bit Server VM sun.os.patch.level = unknown sun.java.launcher = SUN_STANDARD user.country = US user.dir = /tmp ......
如果我們希望查看當前Java應用是否有配置某些信息,可以使用命令jinfo -flag 配置選項 pid,例如我們想查看當前應用是否有開啟gc選項,可以使用下面這段命令:
jinfo -flag PrintGC 19946
可以看到輸出結果為-XX:-PrintGC,因為PrintGC前面是減號,這說明該選項并沒有開啟。
-XX:-PrintGC
如果我們希望將這個選項開啟,我們只需在參數前面加個+號即可,例如我們希望開啟gc選項,我們只需鍵入如下命令:
jinfo -flag +PrintGC 19946
再次查看可以發現,選項生效了:
[root@xxxx tmp]# jinfo -flag PrintGC 19946
-XX:+PrintGC
有些參數是鍵值對的形式,例如我們想配置dump日志的路徑,我們也可以使用jinfo進行配置,命令格式為jinfo -flag 參數=值 Java進程id:
jinfo -flag HeapDumpPath=/tmp/dump.log 19946
打印JVM選項信息可用指令jinfo -flags pid:
Attaching to process ID 4854, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.202-b08
Non-default VM flags: -XX:CICompilerCount=2 -XX:HeapDumpPath=null -XX:InitialHeapSize=33554432 -XX:MaxHeapSize=511705088 -XX:MaxNewSize=170524672 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=11141120 -XX:OldSize=22413312 -XX:+PrintGC -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps
Command line:
查看應用屬性,命令格式jinfo -sysprops Java進程id:
jinfo -sysprops 2341jinfo -sysprops 2341
jmap(查看堆區信息、對象信息等)
在沒有arthas之前,我們打印內存快照大部分都是通過jmap,大體來說jmap支持如下幾個功能:
- 查看使用的GC算法,堆的配置信息以及各個內存區域的內存使用情況
- 顯示堆對象的統計信息,包括每一個Java類、對象數量、內存大小、類名稱等
- 打印等會回收的對象的信息
- 生成dump文件,配合jhat或者mat使用
查看堆內存使用情況我們可以使用jmap -heap Java進程id,例如:
jmap -heap 25534
對應的我們就可以看到當前java進程堆內存的使用情況:
Attaching to process ID 25534, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.202-b08
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 511705088 (488.0MB)
NewSize = 11141120 (10.625MB)
MaxNewSize = 170524672 (162.625MB)
OldSize = 22413312 (21.375MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
查看存活的Java對象(文檔說明:to print histogram of java object heap; if the "live" suboption is specified, only count live objects),命令格式:jmap -histo:live Java進程id,所以我們的指令即:
jmap -histo:live 25534
輸出結果如下,我們可以很直觀的看到各個實例對應占用的內存空間大?。?/p>
num #instances #bytes class name
----------------------------------------------
1: 48676 7974952 [C
2: 7762 1873312 [I
3: 47785 1146840 java.lang.String
4: 12737 1120856 java.lang.reflect.Method
5: 8773 968912 java.lang.Class
6: 25572 818304 java.util.concurrent.ConcurrentHashMap$Node
7: 14108 564320 java.util.LinkedHashMap$Entry
8: 2712 509536 [B
9: 9308 494936 [Ljava.lang.Object;
10: 6345 493128 [Ljava.util.HashMap$Node;
11: 7001 392056 java.util.LinkedHashMap
12: 11255 360160 java.util.HashMap$Node
13: 15946 354528 [Ljava.lang.Class;
14: 18176 290816 java.lang.Object
15: 3447 248184 java.lang.reflect.Field
16: 124 192320 [Ljava.util.concurrent.ConcurrentHashMap$Node;
打印正在被回收的類**(文檔說明:to print information on objects awaiting finalization)**,命令格式:jmap -finalizerinfo Java進程id,所以我們的指令就是:
jmap -finalizerinfo 25534
對此我們既可以看到正在被打印的對象數量等信息:
Attaching to process ID 25534, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.202-b08
Number of objects pending for finalization: 0
dump也就是我們生成內存快照的指令,比較使用,如下所示,這就是將存活的對象的信息存到二進制文件heap.bin中:
jmap -dump:live,format=b,file=/tmp/heap.bin 25534
此時就可以使用jhat打開該文件:
jhat heap.bin
如下所示jhat 文件名,這時候我們就可以通過7000端口查看詳情了:
Reading from heap.bin... Dump file created Wed Nov 02 20:21:18 CST 2022 Snapshot read, resolving... Resolving 346825 objects... Chasing references, expect 69 dots..................................................................... Eliminating duplicate references..................................................................... Snapshot resolved. Started HTTP server on port 7000 Server is ready.
jstat(常用,監控運行時狀態信息)
jstat用于監控虛擬機各種運行狀態信息,顯示虛擬機進程中裝載、內存、垃圾收集、JIT編譯等運行數據。
查看類加載信息,命令格式jstat -class Java進程id,以我們的進程為例,對應的指令就是:
jstat -class 2341
輸出結果如下,我們可以看到如下輸出結果:
Loaded Bytes Unloaded Bytes Time
8221 14604.3 1 0.9 12.74
對應的含義為:
Loaded: 當前已加載的類的數量。 Bytes: 已加載類所占用的字節數。 Unloaded: 卸載的類的數量。 Bytes: 卸載的類所釋放的字節數。 Time: 加載和卸載類所花費的時間(以秒為單位)。
查看編譯統計信息jstat -compiler Java進程id,對應我們進程的使用指令就是:
jstat -compiler 2341
對應的輸出結果如下,其中這各個參數的含義分別是:
Compiled: 已編譯的方法數量。 Failed: 編譯失敗的方法數量。 Invalid: 因為各種原因被標記為無效的方法數量。 Time: 編譯所花費的時間(以毫秒為單位)。 FailedType: 最后一次編譯失敗的原因類型。 FailedMethod: 最后一次編譯失敗的方法名稱及其簽名。
Compiled Failed Invalid Time FailedType FailedMethod
4177 0 0 17.68 0
查看gc統計信息,該信息我們只需通過jstat的gc選項即可查看了:
jstat -gc 2341
對應輸出結果如下每個表頭的含義如下
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 1408.0 1408.0 0.0 1020.3 11840.0 7493.1 29268.0 22168.5 42840.0 40613.5 5760.0 5311.9 65 0.401 2 0.213 0.613
這里我們也給出上文中各個字段的含義:
- S0C :年輕代中第一個survivor(幸存區)的容量 (字節)
- S1C :年輕代中第二個survivor(幸存區)的容量 (字節)
- S0U:年輕代中第一個survivor(幸存區)目前已使用空間 (字節)
- S1U:年輕代中第二個survivor(幸存區)目前已使用空間 (字節)
- EC:年輕代中Eden(伊甸園)的容量 (字節)
- EU:年輕代中Eden(伊甸園)目前已使用空間 (字節)
- OC:Old代的容量 (字節)
- OU:Old代目前已使用空間 (字節)
- PC:Perm(持久代)的容量 (字節)
- PU:Perm(持久代)目前已使用空間 (字節)
- YGC:從應用程序啟動到采樣時年輕代中gc次數
- YGCT:從應用程序啟動到采樣時年輕代中gc所用時間(s)
- FGC:從應用程序啟動到采樣時old代(全gc)gc次數
- FGCT:從應用程序啟動到采樣時old代(全gc)gc所用時間(s)
- GCT:從應用程序啟動到采樣時gc用的總時間(s)
查看gc內存容量和元空間容量:
jstat -gccapacity 2341
對應的輸出結果如下,這樣我們就可以很直觀查看到如下幾個指標:
- NGCMN: 新生代最小容量(New Generation Minimum Capacity)。
- NGCMX: 新生代最大容量(New Generation Maximum Capacity)。
- NGC: 當前新生代容量(Current New Generation Capacity)。
- S0C: 生存空間 0 容量(Survivor Space 0 Capacity)。
- S1C: 生存空間 1 容量(Survivor Space 1 Capacity)。
- EC: Eden 區域容量(Eden Space Capacity)。
- OGCMN: 老年代最小容量(Old Generation Minimum Capacity)。
- OGCMX: 老年代最大容量(Old Generation Maximum Capacity)。
- OGC: 當前老年代容量(Current Old Generation Capacity)。
- MCMN: 元空間最小容量(Metaspace Minimum Capacity)。
- MCMX: 元空間最大容量(Metaspace Maximum Capacity)。
- MC: 當前元空間容量(Current Metaspace Capacity)。
對應的輸出結果如下:
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
10880.0 166528.0 14656.0 1408.0 1408.0 11840.0 21888.0 333184.0 29268.0 29268.0 0.0 1087488.0 42840.0 0.0 1048576.0 5760.0 65 2
查看年輕代統計信息:
jstat -gcnew 2341
對應表頭各個參數含義為:
- S0C: 生存空間 0 容量(Survivor Space 0 Capacity)。
- S1C: 生存空間 1 容量(Survivor Space 1 Capacity)。
- S0U: 生存空間 0 已使用容量(Survivor Space 0 Used)。
- S1U: 生存空間 1 已使用容量(Survivor Space 1 Used)。
- TT: 晉升閾值(Tenuring Threshold),表示對象在新生代中存活多少次 Minor GC 后會被晉升到老年代。
- MTT: 最大晉升閾值(Maximum Tenuring Threshold)。
- DSS: 欲望的生存空間大?。―esired Survivor Size),即期望的 Survivor 空間大小。
- EC: Eden 區域容量(Eden Space Capacity)。
- EU: Eden 區域已使用容量(Eden Space Used)。
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
1408.0 1408.0 0.0 1020.3 2 15 704.0 11840.0 7652.5 65 0.401