JVM监控-命令行篇
1. 概述
性能診斷是軟件工程師在日常工作中需要經常面對和解決的問題,在用戶體驗至上的今天,解決好應用的性能問題能帶來非常大的收益。
Java作為最流行的編程語言之一,其應用性能診斷一直受到業界廣泛關注。可能造成Java出現性能問題的因素非常多,例如線程控制、磁盤讀寫、數據庫訪問、網絡I/O,垃圾收集等。想要定位這些問題,一款優秀的性能診斷工具必不可少。
2. jps:查看正在運行的java進程
2.1 基本情況
jps(Java Process Status):
顯示指定系統內所有的HotSpot虛擬機進程,可用于查詢正在運行的虛擬機進程。
說明:對于本地的虛擬機進程來說,進程的本地虛擬機ID與操作系統的進程ID是一致的,是唯一的。
2.2 基本語法
jps [options] [hostid]
我們還可以通過追加參數,來打印額外的信息
1)基本語法之:options參數
-
-q: 僅僅顯示LVMID(local virtual machine id),即本地虛擬機唯一id。不顯示主類的名稱等
-
-l:輸出應用程序主類的全類名 或 如果進程執行的是jar包,則輸出jar包完整路徑
-
-m:輸出虛擬機進程啟動時傳遞給主類main()的參數
-
-v:列出虛擬機進程啟動時的JVM參數
上述命令可以組合使用
補充:
如果某Java進程關閉了默認開啟的UsePerfData參數(即使用參數 -XX: -UsePerfData),那么jps命令將無法探知該Java進程。
2)基本語法之: hostid參數
RMI注冊表中注冊的主機名。
如果想要遠程監控主機上的java程序,需要安裝 jstatd。
對于具有更嚴格的安全實踐的網絡場所而言,可能是用一個自定義的策略文件來顯示對特定的可信主機或網絡的訪問,盡管這種技術容易受到ip地址欺詐攻擊。
對于安全問題無法使用一個定制的策略文件來處理,那么最安全的操作是不允許jstatd服務器,而是在本地使用jstat和jps工具。
3. jstat:查看JVM統計信息
2.1 基本情況
jstat(JVM Statistics Monitoring Tool):用戶監視虛擬機各種運行狀態信息的命令行工具。它可以顯示本地或者遠程虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據。
在沒有GUI圖形頁面,只提供了純文本控制臺環境的服務器上,它將是運行期定位虛擬機性能問題的首選工具。常用于檢測垃圾回收問題以及內存泄漏問題。
2.2 基本語法
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
1)基本語法之:options參數
-
類裝載相關的:
-
-class:顯示ClassLoader的相關信息:類的裝載、卸載數量、總空間、類裝載所消耗的時間等
-
-
垃圾回收相關的:
-
-gc:顯示與GC相關的堆信息。包括Eden區、兩個Survivor區、老年代、永久代等的容量、已用空間、GC時間合計等信息
-
-gccapacity:顯示內容與-gc基本相同,但輸出主要關注Java堆各個區域使用到的最大、最小空間
-
-gcutil:顯示內容與-gc基本相同,但輸出主要關注已使用空間占總空間的百分比
-
-gccause:與-gcutil功能相同,但輸出主要關注已使用空間占總空間的百分比
-
-gcnew:顯示新生代GC狀況
-
-gcnewcapacity:顯示內容與-gcnew基本相同,輸出主要關注使用到的最大、最小空間
-
-gcold:顯示老年代GC情況
-
-gcoldcapacity:顯示內容與-gcold基本相同,輸出主要關注使用到的最大、最小空間
-
-gcpermcapacity:顯示永久代使用到的最大、最小空間
-
-
JIT相關的:
-
-compiler:顯示JIT編譯器編譯過的方法、耗時等信息
-
-printcompilation:輸出已經被JIT編譯的方法
-
2)基本語法之:interval參數
用于指定輸出統計數據的周期,單位為毫秒
3)基本語法之:count參數
用戶指定查詢的總次數
4)基本語法之:t參數
可以在輸出信息前加一個Timestamp列,顯示程序的運行時間,單位為秒
5)基本語法值:h參數
可以指定在周期性數據輸出時,輸出多少行數據后輸出一個表頭信息
2.3 -gc 輸出的相關數據
4. jinfo:查看和修改JVM配置參數
4.1 基本情況
查看虛擬機配置參數信息,也可用于調整虛擬機的配置參數。
在很多情況下,Java應用程序不會指定所有的Java虛擬機參數。而此時,開發人員可能不知道某一個具體的Java虛擬機參數的默認值。在這種情況下,可能需要通過查找文檔來獲取某個參數的默認值。這個查找過程可能是非常艱難的。但有了jinfo工具,開發人員可以很方便的找到Java虛擬機參數的當前值。
4.2 基本語法
jinfo [options] pid
說明:java進程ID必須要加上
| no option | 輸出全部的參數和系統屬性 |
| -flag name | 輸出對應名稱的參數 |
| -flag [+-]name | 開啟或者關閉對應名稱的參數,只有被標記為manageable的參數才可以動態修改 |
| -flag name=value | 設定對應名稱的參數 |
| -flags | 輸出全部的參數 |
| -sysprops | 輸出系統屬性 |
4.3 修改參數
jinfo不僅可以查看運行時某一個Java虛擬機參數的實際取值,甚至可以在運行時修改部分參數,并使之立即生效。
但是,并非所有參數都支持動態修改。參數只有被標記為manageable的flag可以被試試修改。
查看被標記為manageable的參數命令:
java -XX:+PrintFlagsFinal -version | grep manageable (grep為linux命令)
5. jmap:導出內存映像文件&內存使用情況
5.1基本情況
jmap(JVM Memory Map):作用一方面是獲取dump文件(堆轉儲快照文件),它還可以獲取目標Java進程的內存相關信息,包括Java堆各區域的使用情況、堆中對象的統計信息、類加載信息等。
5.2 基本語法
-
jmap [option] <pid>
-
jmap [option] <executable <core>
-
jmap [option] [server_id@]<remote server IP or hostname>
其中option包括:
| -dump | 生成dump文件 |
| -finalizerinfo | 以ClassLoader為統計口徑輸出永久代的內存狀態信息 |
| -heap | 輸出整個堆空間的詳細信息 |
| -histo | 輸出堆空間中對象的統計信息,包括類、實例數量和合計容量(僅Linux\solaris平臺使用) |
| -permstat | 以ClassLoader為統計口徑輸出永久代的內存狀態信息(僅Linux\solaris平臺使用) |
| -F | 當虛擬機進程對-dump進程沒有任何響應時,強制執行生成dump文件(僅Linux\solaris平臺使用) |
5.3 導出dump文件
手動:
-
jmap -dump:format=b,file=<filename.hprof> <pid>
-
jmap -dump:live,format=b,file=<filename.hprof> <pid> 只會導出存活對象,文件大小不會很大,推薦該方法
自動:
當程序發生OOM退出系統時,一些瞬時信息都隨著程序的終止而消失,而重現OOM問題往往比較困難或耗時。此時若能在OOM時,自動導出dump文件就顯得非常迫切
-
-XX:+HeapDumpOnOutOfMemoryError 當程序發生OOM時,導出應用程序的當前堆快照
-
-XX:HeapDumpPath=<filename.hprof> 可以指定堆快照的保存位置
5.4 自動導出dump演示
我們寫一段java代碼,并設置啟動JVM命令
-Xms60m -Xmx60m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\m.hprof
? ?public static void main(String[] args) {ArrayList<byte[]> list = new ArrayList<>(); ?for (int i = 0; i < 1000; i++) {byte[] arr = new byte[1024 * 100];//100KBlist.add(arr);try {Thread.sleep(60);} catch (InterruptedException e) {e.printStackTrace();}}}一段時間后,發現在我們制定的路徑D盤下,生成了一個m.hprof的dump文件。
我們可以使用圖形化工具來打開它,下面我們用JProfile打開。
6. jhat:JDK自帶堆分析工具
Sun JDK提供的jhat命令與jmap命令搭配使用,用戶分析jmap生成的heap dump文件。jhat內置了一個微型的HTTP/HTML服務器,生成dump文件的分析結果后,用戶可以在瀏覽器中查看分析結果。
使用了jhat命令,就啟動了一個http服務,端口是7000,即 http://localhost:7000/
說明:jhat命令在JDK9中已經被刪除,官方建議用VisualVM代替
7. jstack:打印JVM中線程快照
7.1 基本情況
jstack(JVM Stack Trace):用于生成虛擬機指定進程當前時刻的線程快照(虛擬機堆棧跟蹤)。線程快照就是當前虛擬機內指定進程的每一條線程正在執行的方法堆棧的集合。
作用:可用于定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間等待等問題。這些都是導致線程長時間停頓的常見原因。當線程出現停頓時,就可以用jstack顯示各個線程調用的堆棧情況。
在thread dump中,要留意下面幾種狀態
-
死鎖,DeadLock(重點關注)
-
等待資源,Waiting on condition(重點關注)
-
等待獲取監視器,Waiting on monitor entry(重點關注)
-
阻塞,Blocked(重點關注)
-
執行中,Runable
-
暫停,Suspended
7.2 基本語法
jstack [option] pid
| no option | 正常輸出線程堆棧 |
| -F | 當正常輸出的請求不被響應時,強制輸出線程堆棧 |
| -l | 除堆棧外,顯示關于鎖的附加信息 |
| -m | 如果調用到本地方法的話,可以顯示C/C++的堆棧 |
| -h | 幫助操作 |
7.3 死鎖模擬分析
我們寫一段死鎖代碼:
public class ThreadDeadLock { ?public static void main(String[] args) { ?StringBuilder s1 = new StringBuilder();StringBuilder s2 = new StringBuilder(); ?new Thread(){@Overridepublic void run() { ?synchronized (s1){ ?s1.append("a");s2.append("1"); ?try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();} ?synchronized (s2){s1.append("b");s2.append("2"); ?System.out.println(s1);System.out.println(s2);} ?} ?}}.start(); ? ?new Thread(new Runnable() {@Overridepublic void run() {synchronized (s2){ ?s1.append("c");s2.append("3"); ?try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();} ?synchronized (s1){s1.append("d");s2.append("4"); ?System.out.println(s1);System.out.println(s2);}}}}).start(); ?try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} ?new Thread(new Runnable() {@Overridepublic void run() {Map<Thread, StackTraceElement[]> all = Thread.getAllStackTraces();//追蹤當前進程中的所有的線程Set<Map.Entry<Thread, StackTraceElement[]>> entries = all.entrySet();for(Map.Entry<Thread, StackTraceElement[]> en : entries){Thread t = en.getKey();StackTraceElement[] v = en.getValue();System.out.println("【Thread name is :" + t.getName() + "】");for(StackTraceElement s : v){System.out.println("\t" + s.toString());}}}}).start();} }然后進行jstack分析
可以發現Thread-1和Thread-0處于Blocked狀態
jstack同時也分析出了這兩個線程為死鎖狀態。
8. jcmd多功能命令行
8.1 基本情況
在JDK1.7以后,新增了一個命令行工具jcmd。
它是一個多功能的工具,可以用來實現前面除了jstat之外所有命令的功能。比如:用它來導出堆、內存使用、查看Java進程、導出線程信息、執行GC、JVM運行時間等。
jcmd擁有jmap的大部分功能,并且Oracle官方也推薦使用jcmd代替jmap命令。
8.2 基本語法
-
jcmd -l:列出所有的JVM進程
-
jcmd pid help:針對指定的進程,列出所有支持的命令
-
jcmd pid 具體命令:顯示指定進程的指令命令的數據
總結
以上是生活随笔為你收集整理的JVM监控-命令行篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ElasticSearch中文分词器-I
- 下一篇: java常见的内存泄漏