Java之JVM監控工具分享
VM的基本知識常用的也就是類加載機制,記憶體區域、配置設定、OOM,GC,JVM參數調優
幾個連結自己看:
記憶體區域&類加載機制
配置設定政策&垃圾回收算法、收集器
今天結合代碼講一講常用的java自帶工具講解,這些指令一般都是jdk/lib/tools.jar中。用來監控診斷我們的Java環境。
官方說明:
https://docs.oracle.com/en/java/javase/11/tools/-
jps
顯示目前使用者的所有java程序的PID 以及主類名
jps : 顯示目前使用者的所有java程序的PID 以及主類名
jps -v : 列印傳遞給 Java 虛拟機的參數(如-XX:+UnlockExperimentalVMOptions -XX:+UseZGC)
jps -m : 列印傳遞給主類的參數
jps -l : 列印子產品名以及包名
預設開啟(UsePerfData),若加上-XX:-UsePerfData 則無法找到程序。
-
jstack
功能 jstack不僅會列印線程的棧軌迹、線程狀态(BLOCKED)、持有的鎖(locked…)以及正在請求的鎖(waiting to lock …),而且還會分析出具體的死鎖。
jstack pid : 檢視線程情況
jstack -F pid : 正常輸出不被響應時,使用該指令
jstack -l pid : 除堆棧外,顯示關于鎖的附件資訊
-
jstat
功能 允許使用者檢視目标 Java 程序的類加載、即時編譯以及垃圾回收相關的資訊。它常用于檢測垃圾回收GC問題以及記憶體洩漏問題。
顯示程序中的類裝載、記憶體、垃圾收集、JIT編譯等運作資料。
常用指令
jstat -class pid : 列印類裝載、類解除安裝、總空間以及所消耗的時間
jstat -compiler pid : 列印即時編譯相關的資料
jstat -printcompilation pid : 列印即時編譯相關的資料
jstat -gc pid 1s 20 : 查詢垃圾收集情況,每1秒查詢一次,一共查詢20次。
jstat -gccause pid : 額外輸出上次GC原因
...剩下的都是以-gc為字首的子指令,它們将列印垃圾回收相關的資料。
加上 -t參數 每行資料之前列印目标 Java 程序的啟動時間
我們可以看到,這兩個 Survivor 區的容量相等,而且始終有一個 Survivor 區的記憶體使用量為 0。
在這種情況下,Java 虛拟機會将這塊記憶體區域回收,并标記為可配置設定的狀态。這樣子做的結果是,堆中可能完全沒有 Survivor 記憶體區域,因而相應的 S1C 和 S1U 将會是 0。
我們可以比較 Java 程序的啟動時間以及總 GC 時間(GCT 列),或者兩次測量的間隔時間以及總GC時間的增量,來得出 GC 時間占運行時間的比例。
如果該比例超過 20%,則說明目前堆的壓力較大;如果該比例超過 90%,則說明堆里幾乎沒有可用空間,随時都可能抛出 OOM 異常。
jstat還可以用來判斷是否出現記憶體洩漏。在長時間運行的 Java 程式中,我們可以運行jstat指令連續擷取多行性能資料,并取這幾行資料中 OU 列(即已占用的老年代記憶體)的最小值。
然後,我們每隔一段較長的時間重複一次上述操作,來獲得多組 OU 最小值。如果這些值呈上漲趨勢,則說明該 Java 程式的老年代記憶體已使用量在不斷上漲,這意味着無法回收的對象在不斷增加,是以很有可能存在記憶體洩漏。
CGC 和 CGCT,它們分别代表并發 GC Stop-The-World 的次數和時間。
S0C:年輕代中第一個survivor(幸存區)的容量 (kb)
S1C:年輕代中第二個survivor(幸存區)的容量 (kb)
S0U:年輕代中第一個survivor(幸存區)目前已使用空間 (kb)
S1U:年輕代中第二個survivor(幸存區)目前已使用空間 (kb)
EC:年輕代中Eden(伊甸園)的容量 (kb)
EU:年輕代中Eden(伊甸園)目前已使用空間 (kb)
OC:老年代的容量 (kb)
OU:老年代目前已使用空間 (kb)
MC:元空間的容量 (kb)
MU:元空間目前已使用空間 (kb)
CCSC:壓縮類的容量 (kb)
CCSU:壓縮類目前已使用空間 (kb)
YGC:年輕代垃圾回收次數
YGCT:年輕代垃圾回收消耗時間
FGC:老年代垃圾回收次數
FGCT:老年代垃圾回收消耗時間
GCT:垃圾回收消耗總時間
-
jmap
功能 生成堆轉儲快照(heapdump) 使用者統計目标 Java 程序的堆中存放的 Java 對象,并将它們導出成二進制檔案。查詢Java堆和永久代的詳細資訊,使用率,使用大小,查詢finalize執行隊列的資訊
jmap -heap pid : 列印jvm heap的情況
jmap -histo pid : 列印jvm heap的直方圖。其輸出資訊包括類名,對象數量,對象占用大小。 并按照記憶體使用量從多至少的順序排列
jmap -histo:live pid : JVM會先觸發gc,然後再統計資訊,隻統計堆中的存活對象的情況
jmap -dump:format=b,file=map.log pid: 将記憶體使用的詳細情況輸出到檔案,之後一般使用其他工具進行分析。同樣,-dump:live隻儲存堆中的存活對象。
jmap -clstats pid : 列印被加載類的資訊
jmap -finalizerinfo pid : 該子指令将列印所有待 finalize 的對象。
jmap -permstat pid : 列印permanent generation heap情況
我們通常會利用jmap -dump:live,format=b,file=filename.bin指令,将堆中所有存活對象導出至一個檔案之中。
這里format=b将使jmap導出與hprof(在 Java 9 中已被移除)、-XX:+HeapDumpAfterFullGC、-XX:+HeapDumpOnOutOfMemoryError格式一緻的檔案。這種格式的檔案可以被其他 GUI 工具檢視。
jmap(以及jinfo、jstack和jcmd)依賴于 Java 虛拟機的Attach API,是以隻能監控本地 Java 程序。
一旦開啟 Java 虛拟機參數DisableAttachMechanism(即使用參數-XX:+DisableAttachMechanism),基于 Attach API 的指令将無法執行。反過來說,如果你不想被其他程序監控,那麼你需要開啟該參數。
-
jhat
功能 一般與jmap搭配使用,用來分析jmap生成的堆轉儲檔案。
由于有很多可視化工具(Eclipse Memory Analyzer 、IBM HeapAnalyzer)可以替代,是以很少用。不過在沒有可視化工具的機器上也是可用的。
jmap -dump:format=b,file=map.log pid : 将記憶體使用的詳細情況輸出到檔案
jhat map.log : 解析Java堆轉儲檔案,并啟動一個 web server
示範:
https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html-
jinfo
功能 列印目标 Java 程序的配置參數
實時檢視和調整虛拟機參數,可以顯示未被顯示指定的參數的預設值(jps -v 則不能)。
jinfo pid :可用來檢視目标 Java 程序的參數,如傳遞給 Java 虛拟機的-X(即輸出中的 jvm_args)、-XX參數(即輸出中的 VM Flags),以及可在 Java 層面通過System.getProperty擷取的-D參數(即輸出中的 System Properties)。
-
jcmd
可以用來實作前面除了jstat之外所有指令的功能。
詳見 :
https://www.jianshu.com/p/388e35d8a09b- javap
- 是一個能夠将 class 檔案反彙編成人類可讀格式的工具。 ASM位元組碼操作: https://blog.csdn.net/ohcezzz/article/details/78416176
預設情況下 javap 會列印所有非私有的字段和方法,
-p 列印私有的字段和方法。
-v 列印所有資訊。
-c 查閱方法對應的位元組碼
附:
一隻懂JVM參數的狐狸
代碼驗證
@Slf4j
public class JvmTest {
private byte[] memory;
public JvmTest(byte[] memory) {
this.memory = memory;
}
public static void main(String[] args) throws InterruptedException {
GC測試();
//死循環();
//死鎖();
}
public static void GC測試() throws InterruptedException {
for (int i = 1; i < 5; i++) {
byte[] b = new byte[50 * 1024 * 1024];
log.info("配置設定了50M空間給數組");
Thread.sleep(10000);
}
//方法區中常量引用對象 (虛拟機棧(棧幀中的局部變量)中引用的對象 - 方法區中的靜态變量引用的對象 - 本地方法棧中JNI(即一般說的Native方法)中引用的對象)
JvmTest jvmTest = new JvmTest(new byte[50 * 1024 * 1024]);
log.info("配置設定了50M空間給對象");
log.info("調用了System.gc()");
System.gc();
jvmTest = null;
Thread.sleep(10000);
log.info("調用了System.gc()");
System.gc();
Thread.sleep(3000000);
}
public static void 死循環() {
while (true) {
}
}
public static void 死鎖() {
String obj1 = "obj1";
String obj2 = "obj2";
Runnable r1 = () -> {
log.info("r1 running");
while (true) {
synchronized (obj1) {
log.info("r1 lock obj1");
try {
//擷取obj1後先等一會兒,讓Lock2有足夠的時間鎖住obj2
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
log.info("r1 lock obj2");
}
}
}
};
Runnable r2 = () -> {
log.info("r2 running");
while (true) {
synchronized (obj2) {
log.info("r2 lock obj2");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1) {
log.info("r2 lock obj1");
}
}
}
};
Thread a = new Thread(r1);
Thread b = new Thread(r2);
a.start();
b.start();
}
}
原文位址
https://www.cnblogs.com/loveincode/p/10577550.html