JVM調優工具使用手冊
作為Java開發人員,我們肯定知道JDK的bin目錄下有”java.exe”、”javac.exe”這兩個命令工具,這也是我們平時用得最多的工具。但其實bin目錄下還有很多工具,這些工具可以幫助我們進行JVM的調優,幫我們定位找出應用程式運行中產生的問題。下面我們來看看其中一些調優工具。
前置啟動程式
先啟動一個web應用程式,然後用各種JDK自帶命令優化應用。
jps
jps
用jps查看應用進行ID

jinfo
jinfo -flags 92604
此命令可以實時查看和調整虛擬機各項參數。

jmap
此命令可以用來查看記憶體資訊,實例個數以及佔用大小,還可以生成堆轉儲快照。

jmap -histo 14660 #查看歷史生成的實例
jmap -histo:live 14660 #查看當前存活的實例,執行過程中可能會觸發一次full gc
打開log.txt,內容如下:

- num:序號
- instances:實例數量
- bytes:佔用空間大小
- class name:類名稱,[C is a char[],[S is a short[],[I is a int[],[B is a byte[],[[I is a int[][]
堆資訊
可以查看當前應用的堆具體配置、使用資訊。
jmap -heap 92604

轉儲堆記憶體dump
jmap -dump:format=b,file=web.hprof 92604

也可以設置記憶體溢出自動導出dump文件(記憶體很大的時候,可能會導不出來)
- -XX:+HeapDumpOnOutOfMemoryError
- -XX:HeapDumpPath=./ (路徑)
示例程式碼:
public class OOMTest {
public static List<Object> list = new ArrayList<>();
// JVM設置
// -Xms10M -Xmx10M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\jvm.dump
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
int i = 0;
int j = 0;
while (true) {
list.add(new User(i++, UUID.randomUUID().toString()));
}
}
}
jvisualvm
jvisualvm
該命令可以打開一個可視化介面,監視當前運行應用程式、dump文件進行故障分析等功能。
導入上面示例程式碼運行後產生的hprof文件,可以看到User對象實例數名列前茅。

jstack
此命令可以用來跟蹤Java堆棧資訊。用於生成虛擬機當前試課的執行緒快照。快照可以定位執行緒出現長時間停頓的原因,如執行緒間死鎖、死循環、請求外部資源導致的長時間等待等都是導致執行緒長時間停頓的常見原因。
死鎖示例
public class DeadLockTest {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock1) {
try {
System.out.println("thread1 begin");
Thread.sleep(5000);
} catch (InterruptedException e) {
}
synchronized (lock2) {
System.out.println("thread1 end");
}
}
}).start();
new Thread(() -> {
synchronized (lock2) {
try {
System.out.println("thread2 begin");
Thread.sleep(5000);
} catch (InterruptedException e) {
}
synchronized (lock1) {
System.out.println("thread2 end");
}
}
}).start();
System.out.println("main thread end");
}
}
jstack 100516

“Thread-1″:執行緒名
prio=5: java執行緒優先順序
os_prio:作業系統執行緒優先順序
nid=0x18688 執行緒對應的本地執行緒標識nid
java.lang.Thread.State: BLOCKED 執行緒狀態

還可以用jvisualvm自動檢測到死鎖。

找出佔用cpu最高的執行緒堆棧資訊
public class Math {
public static final int initData = 666;
public static User user = new User();
public int compute() { //一個方法對應一塊棧幀記憶體區域
int a = 1;
int b = 2;
int c = (a + b) * 10;
return c;
}
public static void main(String[] args) {
Math math = new Math();
while (true){
math.compute();
}
}
}
1,使用命令top -p ,顯示你的java進程的記憶體情況,pid是你的java進程號,比如18963

2,按H,獲取每個執行緒的記憶體情況

3,找到記憶體和cpu佔用最高的執行緒tid,比如18964
4,轉為十六進位得到 0x4a14,此為執行緒id的十六進位表示
5,執行 jstack 18963|grep -A 10 4a14,得到執行緒堆棧資訊中 4cd0 這個執行緒所在行的後面10行,從堆棧中可以發現導致cpu飆高的調用方法

6,查看對應的堆棧資訊找出可能存在問題的程式碼
jstat
jstat命令可以查看堆記憶體各部分的使用量,以及載入類的數量。命令的格式如下:
jstat [-命令選項] [vmid] [間隔時間(毫秒)] [查詢次數]
注意:使用的jdk版本是jdk8
整體GC壓力情況(常用)
jstat -gc 103784 2000 1000
可以評估程式記憶體使用及GC壓力整體情況,監控gc,每2000ms列印輸出一次,輸出1000次。

- S0C:第一個倖存區的大小,單位KB
- S1C:第二個倖存區的大小
- S0U:第一個倖存區的使用大小
- S1U:第二個倖存區的使用大小
- EC:伊甸園區的大小
- EU:伊甸園區的使用大小
- OC:老年代大小
- OU:老年代使用大小
- MC:方法區大小(元空間)
- MU:方法區使用大小
- CCSC:壓縮類空間大小
- CCSU:壓縮類空間使用大小
- YGC:年輕代垃圾回收次數
- YGCT:年輕代垃圾回收消耗時間,單位s
- FGC:老年代垃圾回收次數
- FGCT:老年代垃圾回收消耗時間,單位s
- GCT:垃圾回收消耗總時間,單位s
堆記憶體統計
jstat -gccapacity 103784

- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:當前新生代容量
- S0C:第一個倖存區大小
- S1C:第二個倖存區的大小
- EC:伊甸園區的大小
- OGCMN:老年代最小容量
- OGCMX:老年代最大容量
- OGC:當前老年代大小
- OC:當前老年代大小
- MCMN:最小元數據容量
- MCMX:最大元數據容量
- MC:當前元數據空間大小
- CCSMN:最小壓縮類空間大小
- CCSMX:最大壓縮類空間大小
- CCSC:當前壓縮類空間大小
- YGC:年輕代gc次數
- FGC:老年代GC次數
新生代垃圾回收統計
jstat -gcnew 103784

新生代記憶體統計
jstat -gcnewcapacity 103784

- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:當前新生代容量
- S0CMX:最大倖存1區大小
- S0C:當前倖存1區大小
- S1CMX:最大倖存2區大小
- S1C:當前倖存2區大小
- ECMX:最大伊甸園區大小
- EC:當前伊甸園區大小
- YGC:年輕代垃圾回收次數
- FGC:老年代回收次數
老年代垃圾回收統計
jstat -gcold 103784

老年代記憶體統計
jstat -gcoldcapacity 103784

元數據空間統計
jstat -gcmetacapacity 103784

- MCMN:最小元數據容量
- MCMX:最大元數據容量
- MC:當前元數據空間大小
- CCSMN:最小壓縮類空間大小
- CCSMX:最大壓縮類空間大小
- CCSC:當前壓縮類空間大小
- YGC:年輕代垃圾回收次數
- FGC:老年代垃圾回收次數
- FGCT:老年代垃圾回收消耗時間
- GCT:垃圾回收消耗總時間
總結
上面介紹到的各個命令,都是JDK提供給我們的最基礎的調優工具,如果還有功能更好的,那也是基於上面的基礎功能進行開發封裝。所以我們只要理解會用上面的各個命令,調優也不是什麼問題。


