JVM 指令集概覽:基礎(chǔ)與應(yīng)用
在現(xiàn)代軟件開發(fā)中,Java 語言憑借其“一次編寫,到處運(yùn)行”的理念成為了企業(yè)級(jí)應(yīng)用的首選之一。這一理念的背后支撐技術(shù)正是 Java 虛擬機(jī)(JVM)。JVM 是一個(gè)抽象的計(jì)算機(jī),它實(shí)現(xiàn)了 Java 編程語言的各種特性,并且能夠執(zhí)行編譯后的字節(jié)碼文件。了解 JVM 的工作原理對(duì)于優(yōu)化程序性能、調(diào)試代碼以及深入理解 Java 應(yīng)用至關(guān)重要。
本文將簡(jiǎn)要介紹 JVM 中常見的指令及其作用,旨在為開發(fā)者提供一個(gè)清晰的認(rèn)識(shí)框架,幫助他們更好地掌握 JVM 指令集的基本概念及其實(shí)戰(zhàn)技巧。我們將從最基礎(chǔ)的加載和存儲(chǔ)指令開始,逐步探討控制轉(zhuǎn)移、方法調(diào)用等更復(fù)雜的操作符,最終帶領(lǐng)讀者深入了解 JVM 內(nèi)部工作機(jī)制的一角。
詳解JVM常見指令
jinfo(查看配置信息)
查看Java應(yīng)用程序配置參數(shù)或者JVM系統(tǒng)屬性,相關(guān)命令詳情我們可以使用-help或者man命令查看:
jinfo -help
對(duì)此我們也給出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
為了演示,筆者在服務(wù)器上開啟了一個(gè)Java應(yīng)用,我們可以使用jps命令查看其進(jìn)程id,可以看到筆者服務(wù)器中有一個(gè)pid為19946的Java進(jìn)程,查看當(dāng)前應(yīng)用所有的配置參數(shù)以及系統(tǒng)配置屬性命令為jinfo pid,可以看到通過jinfo結(jié)合進(jìn)程號(hào)即可看到這個(gè)java進(jìn)程對(duì)應(yīng)的各自類庫(kù)、版本號(hào)以及系統(tǒng)信息:
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 ......
如果我們希望查看當(dāng)前Java應(yīng)用是否有配置某些信息,可以使用命令jinfo -flag 配置選項(xiàng) pid,例如我們想查看當(dāng)前應(yīng)用是否有開啟gc選項(xiàng),可以使用下面這段命令:
jinfo -flag PrintGC 19946
可以看到輸出結(jié)果為-XX:-PrintGC,因?yàn)镻rintGC前面是減號(hào),這說明該選項(xiàng)并沒有開啟。
-XX:-PrintGC
如果我們希望將這個(gè)選項(xiàng)開啟,我們只需在參數(shù)前面加個(gè)+號(hào)即可,例如我們希望開啟gc選項(xiàng),我們只需鍵入如下命令:
jinfo -flag +PrintGC 19946
再次查看可以發(fā)現(xiàn),選項(xiàng)生效了:
[root@xxxx tmp]# jinfo -flag PrintGC 19946
-XX:+PrintGC
有些參數(shù)是鍵值對(duì)的形式,例如我們想配置dump日志的路徑,我們也可以使用jinfo進(jìn)行配置,命令格式為jinfo -flag 參數(shù)=值 Java進(jìn)程id:
jinfo -flag HeapDumpPath=/tmp/dump.log 19946
打印JVM選項(xiàng)信息可用指令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:
查看應(yīng)用屬性,命令格式j(luò)info -sysprops Java進(jìn)程id:
jinfo -sysprops 2341jinfo -sysprops 2341
jmap(查看堆區(qū)信息、對(duì)象信息等)
在沒有arthas之前,我們打印內(nèi)存快照大部分都是通過jmap,大體來說jmap支持如下幾個(gè)功能:
- 查看使用的GC算法,堆的配置信息以及各個(gè)內(nèi)存區(qū)域的內(nèi)存使用情況
- 顯示堆對(duì)象的統(tǒng)計(jì)信息,包括每一個(gè)Java類、對(duì)象數(shù)量、內(nèi)存大小、類名稱等
- 打印等會(huì)回收的對(duì)象的信息
- 生成dump文件,配合jhat或者mat使用
查看堆內(nèi)存使用情況我們可以使用jmap -heap Java進(jìn)程id,例如:
jmap -heap 25534
對(duì)應(yīng)的我們就可以看到當(dāng)前java進(jìn)程堆內(nèi)存的使用情況:
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對(duì)象(文檔說明:to print histogram of java object heap; if the "live" suboption is specified, only count live objects),命令格式:jmap -histo:live Java進(jìn)程id,所以我們的指令即:
jmap -histo:live 25534
輸出結(jié)果如下,我們可以很直觀的看到各個(gè)實(shí)例對(duì)應(yīng)占用的內(nèi)存空間大小:
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進(jìn)程id,所以我們的指令就是:
jmap -finalizerinfo 25534
對(duì)此我們既可以看到正在被打印的對(duì)象數(shù)量等信息:
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也就是我們生成內(nèi)存快照的指令,比較使用,如下所示,這就是將存活的對(duì)象的信息存到二進(jìn)制文件heap.bin中:
jmap -dump:live,format=b,file=/tmp/heap.bin 25534
此時(shí)就可以使用jhat打開該文件:
jhat heap.bin
如下所示jhat 文件名,這時(shí)候我們就可以通過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(常用,監(jiān)控運(yùn)行時(shí)狀態(tài)信息)
jstat用于監(jiān)控虛擬機(jī)各種運(yùn)行狀態(tài)信息,顯示虛擬機(jī)進(jìn)程中裝載、內(nèi)存、垃圾收集、JIT編譯等運(yùn)行數(shù)據(jù)。
查看類加載信息,命令格式j(luò)stat -class Java進(jìn)程id,以我們的進(jìn)程為例,對(duì)應(yīng)的指令就是:
jstat -class 2341
輸出結(jié)果如下,我們可以看到如下輸出結(jié)果:
Loaded Bytes Unloaded Bytes Time
8221 14604.3 1 0.9 12.74
對(duì)應(yīng)的含義為:
Loaded: 當(dāng)前已加載的類的數(shù)量。 Bytes: 已加載類所占用的字節(jié)數(shù)。 Unloaded: 卸載的類的數(shù)量。 Bytes: 卸載的類所釋放的字節(jié)數(shù)。 Time: 加載和卸載類所花費(fèi)的時(shí)間(以秒為單位)。
查看編譯統(tǒng)計(jì)信息jstat -compiler Java進(jìn)程id,對(duì)應(yīng)我們進(jìn)程的使用指令就是:
jstat -compiler 2341
對(duì)應(yīng)的輸出結(jié)果如下,其中這各個(gè)參數(shù)的含義分別是:
Compiled: 已編譯的方法數(shù)量。 Failed: 編譯失敗的方法數(shù)量。 Invalid: 因?yàn)楦鞣N原因被標(biāo)記為無效的方法數(shù)量。 Time: 編譯所花費(fèi)的時(shí)間(以毫秒為單位)。 FailedType: 最后一次編譯失敗的原因類型。 FailedMethod: 最后一次編譯失敗的方法名稱及其簽名。
Compiled Failed Invalid Time FailedType FailedMethod
4177 0 0 17.68 0
查看gc統(tǒng)計(jì)信息,該信息我們只需通過jstat的gc選項(xiàng)即可查看了:
jstat -gc 2341
對(duì)應(yīng)輸出結(jié)果如下每個(gè)表頭的含義如下
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
這里我們也給出上文中各個(gè)字段的含義:
- S0C :年輕代中第一個(gè)survivor(幸存區(qū))的容量 (字節(jié))
- S1C :年輕代中第二個(gè)survivor(幸存區(qū))的容量 (字節(jié))
- S0U:年輕代中第一個(gè)survivor(幸存區(qū))目前已使用空間 (字節(jié))
- S1U:年輕代中第二個(gè)survivor(幸存區(qū))目前已使用空間 (字節(jié))
- EC:年輕代中Eden(伊甸園)的容量 (字節(jié))
- EU:年輕代中Eden(伊甸園)目前已使用空間 (字節(jié))
- OC:Old代的容量 (字節(jié))
- OU:Old代目前已使用空間 (字節(jié))
- PC:Perm(持久代)的容量 (字節(jié))
- PU:Perm(持久代)目前已使用空間 (字節(jié))
- YGC:從應(yīng)用程序啟動(dòng)到采樣時(shí)年輕代中g(shù)c次數(shù)
- YGCT:從應(yīng)用程序啟動(dòng)到采樣時(shí)年輕代中g(shù)c所用時(shí)間(s)
- FGC:從應(yīng)用程序啟動(dòng)到采樣時(shí)old代(全gc)gc次數(shù)
- FGCT:從應(yīng)用程序啟動(dòng)到采樣時(shí)old代(全gc)gc所用時(shí)間(s)
- GCT:從應(yīng)用程序啟動(dòng)到采樣時(shí)gc用的總時(shí)間(s)
查看gc內(nèi)存容量和元空間容量:
jstat -gccapacity 2341
對(duì)應(yīng)的輸出結(jié)果如下,這樣我們就可以很直觀查看到如下幾個(gè)指標(biāo):
- NGCMN: 新生代最小容量(New Generation Minimum Capacity)。
- NGCMX: 新生代最大容量(New Generation Maximum Capacity)。
- NGC: 當(dāng)前新生代容量(Current New Generation Capacity)。
- S0C: 生存空間 0 容量(Survivor Space 0 Capacity)。
- S1C: 生存空間 1 容量(Survivor Space 1 Capacity)。
- EC: Eden 區(qū)域容量(Eden Space Capacity)。
- OGCMN: 老年代最小容量(Old Generation Minimum Capacity)。
- OGCMX: 老年代最大容量(Old Generation Maximum Capacity)。
- OGC: 當(dāng)前老年代容量(Current Old Generation Capacity)。
- MCMN: 元空間最小容量(Metaspace Minimum Capacity)。
- MCMX: 元空間最大容量(Metaspace Maximum Capacity)。
- MC: 當(dāng)前元空間容量(Current Metaspace Capacity)。
對(duì)應(yīng)的輸出結(jié)果如下:
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
查看年輕代統(tǒng)計(jì)信息:
jstat -gcnew 2341
對(duì)應(yīng)表頭各個(gè)參數(shù)含義為:
- 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),表示對(duì)象在新生代中存活多少次 Minor GC 后會(huì)被晉升到老年代。
- MTT: 最大晉升閾值(Maximum Tenuring Threshold)。
- DSS: 欲望的生存空間大?。―esired Survivor Size),即期望的 Survivor 空間大小。
- EC: Eden 區(qū)域容量(Eden Space Capacity)。
- EU: Eden 區(qū)域已使用容量(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