第4章 虚拟机性能监控、故障处理工具(上)
Java與C++之間有一堵由內存動態分配和垃圾收集技術所圍成的高墻,墻外面的人想進去,墻里面的人卻想出來。
4.1 概述
經過前面兩章對于虛擬機內存分配與回收技術各方面的介紹,相信讀者已經建立了一個比較系統、完整的理論基礎。理論總是作為指導實踐的工具,把這些知識應用到實際工作中才是我們的最終目的。
接下來的兩章,我們將從實踐的角度去認識虛擬機內存管理的世界。給一個系統定位問題的時候,知識、經驗是關鍵基礎,數據是依據,工具是運用知識處理數據的手段。這里說的數據包括但不限于異常堆棧、虛擬機運行日志、垃圾收集器日志、線程快照(threaddump/javacore文件)、堆轉儲快照(heapdump/hprof文件)等。恰當地使用虛擬機故障處理、分析的工具可以提升我們分析數據、定位并解決問題的效率,但我們在學習工具前,也應當意識到工具永遠都是知識技能的一層包裝,沒有什么工具是“秘密武器”,擁有了就能“包治百病”。
4.2 基礎故障處理工具
Java開發人員肯定都知道JDK的bin目錄中有java.exe、javac.exe這兩個命令行工具,但并非所有程序員都了解過JDK的bin目錄下其他各種小工具的作用。隨著JDK版本的更迭,這些小工具的數量和功能也在不知不覺地增加與增強。除了編譯和運行Java程序外,打包、部署、簽名、調試、監控、運維等各種場景都可能會用到它們,這些工具如圖4-1所示。
在本章,筆者將介紹這些工具中的一部分,主要是用于監視虛擬機運行狀態和進行故障處理的工具。這些故障處理工具并不單純是被Oracle公司作為“禮物”附贈給JDK的使用者,根據軟件可用性和授權的不同,可以把它們劃分成三類:
- 商業授權工具:主要是JMC(Java Mission Control)及它要使用到的JFR(JavaFlight Recorder),JMC這個原本來自于JRockit的運維監控套件從JDK 7 Update40開始就被集成到OracleJDK中,JDK 11之前都無須獨立下載,但是在商業環境中使用它則是要付費的。
- 正式支持工具:這一類工具屬于被長期支持的工具,不同平臺、不同版本的JDK之間,這類工具可能會略有差異,但是不會出現某一個工具突然消失的情況。
- 實驗性工具:這一類工具在它們的使用說明中被聲明為“沒有技術支持,并且是實驗性質的”(Unsupported and Experimental)產品,日后可能會轉正,也可能會在某個JDK版本中無聲無息地消失。但事實上它們通常都非常穩定而且功能強大,也能在處理應用程序性能問題、定位故障時發揮很大的作用。
讀者如果比較細心的話,還可能會注意到這些工具程序大多數體積都異常小。假如之前沒注意到,現在不妨再看看圖4-1中的最后一列“大小”,各個工具的體積基本上都穩定在21KB左右。并非JDK開發團隊刻意把它們制作得如此精煉、統一,而是因為這些命令行工具大多僅是一層薄包裝而已,真正的功能代碼是實現在JDK的工具類庫中的,讀者把圖4-1和圖4-2兩張圖片對比一下就可以看得很清楚。假如讀者使用的是Linux版本的JDK,還可以發現這些工具中不少是由Shell腳本直接寫成,可以用文本編輯器打開并編輯修改它們。
JDK開發團隊選擇采用Java語言本身來實現這些故障處理工具是有特別用意的:當應用程序部署到生產環境后,無論是人工物理接觸到服務器還是遠程Telnet到服務器上都可能會受到限制。借助這些工具類庫里面的接口和實現代碼,開發者可以選擇直接在應用程序中提供功能強大的監控分析功能。
本章所講解的工具大多基于Windows平臺下的JDK進行演示,如果讀者選用的JDK版本、操作系統不同,那么工具不僅可能數量上有所差別,同一個工具所支持的功能范圍和效果都可能會不一樣。本章提及的工具,如無特別說明,是JDK 5中就已經存在的,但為了避免運行環境帶來的差異和兼容性問題,建議讀者使用更高版本的JDK來驗證本章介紹的內容。通常高版本JDK的工具有可能向下兼容運行于低版本JDK的虛擬機上的程序,反之則一般不行。
注意 如果讀者在工作中需要監控運行于JDK 5的虛擬機之上的程序,在程序啟動時請添加參數“-Dcom.sun.management.jmxremote”開啟JMX管理功能,否則由于大部分工具都是基于或者要用到JMX(包括下一節的可視化工具),它們都將無法使用,如果被監控程序運行于JDK 6或以上版本的虛擬機之上,那JMX管理默認是開啟的,虛擬機啟動時無須再添加任何參數。
4.2.1 jps:虛擬機進程狀況工具
JDK的很多小工具的名字都參考了UNIX命令的命名方式,jps(JVM ProcessStatus Tool)是其中的典型。除了名字像UNIX的ps命令之外,它的功能也和ps命令類似:可以列出正在運行的虛擬機進程,并顯示虛擬機執行主類(Main Class,main()函數所在的類)名稱以及這些進程的本地虛擬機唯一ID(LVMID,LocalVirtual Machine Identifier)。雖然功能比較單一,但它絕對是使用頻率最高的JDK命令行工具,因為其他的JDK工具大多需要輸入它查詢到的LVMID來確定要監控的是哪一個虛擬機進程。對于本地虛擬機進程來說,LVMID與操作系統的進程ID(PID,Process Identifier)是一致的,使用Windows的任務管理器或者UNIX的ps命令也可以查詢到虛擬機進程的LVMID,但如果同時啟動了多個虛擬機進程,無法根據進程名稱定位時,那就必須依賴jps命令顯示主類的功能才能區分了。
jps命令格式:
jps [ options ] [ hostid ]jps執行樣例:
jps -l 1168 jdk.jcmd/sun.tools.jps.Jpsjps還可以通過RMI協議查詢開啟了RMI服務的遠程虛擬機進程狀態,參數hostid為RMI注冊表中注冊的主機名。jps的其他常用選項見表4-1。
4.2.2 jstat:虛擬機統計信息監視工具
jstat(JVM Statistics Monitoring Tool)是用于監視虛擬機各種運行狀態信息的命令行工具。它可以顯示本地或者遠程虛擬機進程中的類加載、內存、垃圾收集、即時編譯等運行時數據,在沒有GUI圖形界面、只提供了純文本控制臺環境的服務器上,它將是運行期定位虛擬機性能問題的常用工具。
jstat命令格式為:
jstat -<option> <vmid> [<interval> [<count>]]對于命令格式中的VMID與LVMID需要特別說明一下:如果是本地虛擬機進程,VMID與LVMID是一致的;如果是遠程虛擬機進程,那VMID的格式應當是:
[protocol:][//]lvmid[@hostname[:port].servername]參數interval和count代表查詢間隔和次數,如果省略這2個參數,說明只查詢一次。假設需要每250毫秒查詢一次進程2764垃圾收集狀況,一共查詢20次,那命令應當是:
jstat -gc 2764 250 20選項option代表用戶希望查詢的虛擬機信息,主要分為三類:類加載、垃圾收集、運行期編譯狀況。詳細請參考表4-2中的描述。
jstat監視選項眾多,囿于版面原因無法逐一演示,這里僅舉一個在命令行下監視一臺服務器的內存狀況的例子,用以演示如何查看監視結果。監視參數與輸出結果如代碼清單4-1所示。
查詢結果表明:這臺服務器的新生代Eden區(E,表示Eden)使用了52%的空間,2個Survivor區(S0、S1,表示Survivor0、Survivor1)中S0使用了0.08%的空間,S1里面是空的,老年代(O,表示Old)和元空間(M,表示Metaspace)則分別使用了38.41%和94.74%的空間。程序運行以來共發生Minor GC(YGC,表示Young GC)7466次,總耗時12.813秒;發生Full GC(FGC,表示Full GC)872次,總耗時(FGCT,表示Full GC Time)為148.919秒;所有GC總耗時(GCT,表示GC Time)為161.732秒。
使用jstat工具在純文本狀態下監視虛擬機狀態的變化,在用戶體驗上也許不如后文將會提到的JMC、VisualVM等可視化的監視工具直接以圖表展現那樣直觀,但在實際生產環境中不一定可以使用圖形界面,而且多數服務器管理員也都已經習慣了在文本控制臺工作,直接在控制臺中使用jstat命令依然是一種常用的監控方式。
4.2.3 jinfo:Java配置信息工具
jinfo(Configuration Info for Java)的作用是實時查看和調整虛擬機各項參數。使用jps命令的-v參數可以查看虛擬機啟動時顯式指定的參數列表,但如果想知道未被顯式指定的參數的系統默認值,除了去找資料外,就只能使用jinfo的-flag選項進行查詢了(如果只限于JDK 6或以上版本的話,使用java-XX:+PrintFlagsFinal查看參數默認值也是一個很好的選擇)。jinfo還可以使用-sysprops選項把虛擬機進程的System.getProperties()的內容打印出來。這個命令在JDK 5時期已經隨著Linux版的JDK發布,當時只提供了信息查詢的功能,JDK 6之后,jinfo在Windows和Linux平臺都有提供,并且加入了在運行期修改部分參數值的能力(可以使用-flag[+|-]name或者-flag name=value在運行期修改一部分運行期可寫的虛擬機參數值)。在JDK 6中,jinfo對于Windows平臺功能仍然有較大限制,只提供了最基本的-flag選項。
jinfo命令格式:
jinfo <option> <pid>執行樣例:查詢CMSInitiatingOccupancyFraction參數值
jinfo -flag CMSInitiatingOccupancyFraction 6352 -XX:CMSInitiatingOccupancyFraction=-14.2.4 jmap:Java內存映像工具
jmap(Memory Map for Java)命令用于生成堆轉儲快照(一般稱為 heapdump 或 dump 文件)。如果不使用 jmap 命令,要想獲取Java堆轉儲快照也還有一些比較“暴力”的手段:譬如在第2章中用過的 -XX:+HeapDumpOnOutOfMemoryError 參數,可以讓虛擬機在內存溢出異常出現之后自動生成堆轉儲快照文件,通過 -XX:+HeapDumpOnCtrlBreak 參數則可以使用 [Ctrl] + [Break] 鍵讓虛擬機生成堆轉儲快照文件,又或者在 Linux 系統下通過 Kill -3 命令發送進程退出信號“恐嚇”一下虛擬機,也能順利拿到堆轉儲快照。
jmap的作用并不僅僅是為了獲取堆轉儲快照,它還可以查詢 finalize 執行隊列、Java堆和方法區的詳細信息,如空間使用率、當前用的是哪種收集器等。
和jinfo命令一樣,jmap有部分功能在Windows平臺下是受限的,除了生成堆轉儲快照的-dump選項和用于查看每個類的實例、空間占用統計的-histo選項在所有操作系統中都可以使用之外,其余選項都只能在Linux/Solaris中使用。
jmap命令格式:
jmap -<option> <pid>option選項的合法值與具體含義如表4-3所示。
代碼清單4-2是使用jmap生成一個正在運行的進程的堆轉儲快照文件的例子,例子中的6352是通過jps命令查詢到的LVMID。代碼清單4-2 使用jmap生成dump文件
jmap -dump:format=b,file=test.dump 6352 Heap dump file created4.2.5 jhat:虛擬機堆轉儲快照分析工具
JDK提供jhat(JVM Heap Analysis Tool)命令與jmap搭配使用,來分析jmap生成的堆轉儲快照。jhat內置了一個微型的HTTP/Web服務器,生成堆轉儲快照的分析結果后,可以在瀏覽器中查看。不過實事求是地說,在實際工作中,除非手上真的沒有別的工具可用,否則多數人是不會直接使用jhat命令來分析堆轉儲快照文件的,主要原因有兩個方面。一是一般不會在部署應用程序的服務器上直接分析堆轉儲快照,即使可以這樣做,也會盡量將堆轉儲快照文件復制到其他機器上進行分析,因為分析工作是一個耗時而且極為耗費硬件資源的過程,既然都要在其他機器上進行,就沒有必要再受命令行工具的限制了。另外一個原因是jhat的分析功能相對來說比較簡陋,后文將會介紹到的VisualVM,以及專業用于分析堆轉儲快照文件的Eclipse Memory Analyzer、IBM HeapAnalyzer等工具,都能實現比jhat更強大專業的分析功能。代碼清單4-3演示了使用jhat分析上一節采用jmap生成的內存快照文件。
jhat test.dump Reading from test.dump... Dump file created Wed Jul 20 17:55:02 CST 2022 Snapshot read, resolving... Resolving 865730 objects... Chasing references, expect 173 dots..................................................................................... ........................................................................................ Eliminating duplicate references........................................................................................ ..................................................................................... Snapshot resolved. Started HTTP server on port 7000 Server is ready.屏幕顯示“Server is ready.”的提示后,用戶在瀏覽器中輸入http://localhost:7000/可以看到分析結果
分析結果默認以包為單位進行分組顯示,分析內存泄漏問題主要會使用到其中的“Heap Histogram”(與jmap-histo功能一樣)與OQL頁簽的功能,前者可以找到內存中總容量最大的對象,后者是標準的對象查詢語言,使用類似SQL的語法對內存中的對象進行查詢統計。
4.2.6 jstack:Java堆棧跟蹤工具
jstack(Stack Trace for Java)命令用于生成虛擬機當前時刻的線程快照(一般稱為threaddump或者javacore文件)。線程快照就是當前虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的目的通常是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間掛起等,都是導致線程長時間停頓的常見原因。線程出現停頓時通過jstack來查看各個線程的調用堆棧,就可以獲知沒有響應的線程到底在后臺做些什么事情,或者等待著什么資源。
jstack命令格式:
jstack [-l][-e] <pid>選項的合法值與具體含義如表4-4所示。
代碼清單4-4 使用jstack查看線程堆棧(部分結果)
從JDK 5起,java.lang.Thread類新增了一個getAllStackTraces()方法用于獲取虛擬機中所有線程的StackTraceElement對象。
4.2.7 基礎工具總結
下面表4-5~表4-14中羅列了JDK附帶的全部(包括曾經存在但已經在最新版本中被移除的)工具及其簡要用途,限于篇幅,本節只講解了6個常用的命令行工具。筆者選擇這幾個工具除了因為它們是最基礎的命令外,還因為它們已經有很長的歷史,能適用于大多數讀者工作、學習中使用的JDK版本。在高版本的JDK中,這些工具大多已有了功能更為強大的替代品,譬如JCMD、JHSDB的命令行模式,但使用方法也是相似的,無論JDK發展到了什么版本,學習這些基礎的工具命令并不會過時和浪費。
- 基礎工具:用于支持基本的程序創建和運行
| appletviewer | 在不使用Web瀏覽器的情況下運行和調試Applet,JDK11中被移除 |
| extcheck | 檢查JAR沖突的工具,從JDK9中被移除 |
| jar | 創建和管理JAR文件 |
| java | Java運行工具,用于運行Class文件或JAR文件 |
| javac | 用于Java編程語言的編譯器 |
| javadoc | Java的API文檔生成器 |
| javah | C語言頭文件和Stub函數生成器,用于編寫JNI方法 |
| javap | Java字節碼分析工具 |
| jlink | 將Module和它的依賴打包成一個運行時鏡像文件 |
| jdb | 基于JPDA協議的調試器,以類似于GDB的方式進行調試Java代碼 |
| jdeps | Java類依賴性分析器 |
| jdeprscan | 用于搜索JAR包中使用了“deprecated”的類,從JDK9開始提供 |
- 安全:用于程序簽名、設置安全測試等
| keytool | 管理密鑰庫和證書。主要用于獲取或緩存Kerberos協議的票據授權票據。允許用戶查看本地憑據和密鑰表中的條目(用于Kerberos協議) |
| jarsigner | 生成并驗證JAR簽名 |
| policytool | 管理策略文件的GUI工具,用于管理用戶策略文件(.java.policy),在JDK10中被移除 |
- 國際化:用于創建本地語言文件
| native2ascii | 本地編碼到ASCII編碼的轉換器,用于“任意受支持的字符編碼”和與之對應的“ASCII編碼和Unicode轉義”之間的相互轉換 |
- 遠程方法調用:用于跨Web或網絡的服務交互
| rmic | Java RMI 編譯器,為使用 JRMP 或 IIPO 協議的遠程對象生成Stub、Skeleton 和 Tie 類,也用于生成 OMG IDL |
| rmiregistry | 遠程對象注冊表服務,用于在當前主機的指定端口上創建并啟動一個遠程對象注冊表 |
| rmid | 啟動激活系統守護進程,允許在虛擬機中注冊激活對象 |
| serialver | 生成并返回指定類的序列化版本ID |
- Java IDL與RMI-IIOP:在JDK 11中結束了十余年的CORBA支持,這些工具不再提供
| tnameserv | 提供對命名服務的訪問 |
| idlj | IDL 轉 Java 編譯器,生成映射 OMG IDL 接口的Java源文件,并啟用以Java語言編寫的使用CORBA功能的應用程序的Java源文件。IDL 即接口定義語言 |
| orbd | 對象請求代理守護進程(Object Request Broker Daemon),提供從客戶端查找和調用CORBA環境服務端上的持久化對象的功能。 |
| servertool | 為應用程序注冊、注銷、啟動和關閉服務提供易用的接口 |
- 部署工具:用于程序打包、發布和部署
| Javapackager | 打包、簽名Java和Java FX 應用程序,在JDK11中被移除 |
| pack200 | 使用Java GZIP 壓縮器將JAR文件轉換為壓縮的Pack200文件。壓縮的壓縮文件是高度壓縮的JAR,可以直接部署,節省帶寬并減少下載時間 |
| unpack200 | 將Pack200 生成的打包文件壓縮提取為JAR 文件 |
- Java Web Start
| javaws | 啟動Java Web Start并設置各種選項的工具。在JDK 11中被移除 |
- 性能監控和故障處理:用于監控分析Java虛擬機運行信息,排查問題
| jps | JVM Process Status Tool,顯示指定系統內所有的HotSpot 虛擬機進程 |
| jstat | JVM Statistics Monitoring Tool ,用于收集HotSpot 虛擬機各方面的運行數據 |
| jstatd | JVM Statistics Monitoring Tool Daemon ,jstat的守護程序,啟動一個RMI服務器應用程序,用于監視測試的HotSpot虛擬機的創建和終止,并提供一個界面,允許遠程監控工具附加到在本地系統上運行的虛擬機。在JDK9中集成到了JHSDB中 |
| jinfo | Configuration Info for Java,顯示虛擬機配置信息。在JDK9中集成到了JHSDB中 |
| jmap | Memory Map for Java,生成虛擬機的內存轉儲快照(headdump文件)。在JDK9中集成到了JHSDB中 |
| jhat | JVM Heap Analysis Tool,用于分析堆轉儲快照,它會建立一個HTTP/Web服務器,讓用戶可以在瀏覽器上查看分析結果。在JDK9中集成到了JHSDB中 |
| jhsdb | Java HotSpot Debugger,一個基于Serviceability Agent 的HotSpot 進程調試器,從JDK9 開始提供 |
| jsadebugd | Java Serviceability Agent Debug Daemon,用于Java的可維護性代理調試守護程序,主要用于附加到指定的Java進程、核心文件,或充當一個調試服務器 |
| jcmd | JVM Command,虛擬機診斷命令工具,將診斷命令請求發送到正在運行的Java虛擬機。 |
| jconsole | Java Console ,用于監控Java虛擬機的使用JMX規范的圖形工具。它可以監控本地和遠程Java虛擬機,還可以監控和管理應用程序。 |
| jmc | Java Mission Control , 包含用于監控和管理Java應用程序的工具,而不會引入與這些工具相關聯的性能開銷。開發者可以使用jmc命令來創建JMC工具 |
| jvisualvm | Java VisualVM,一種圖形化工具,可在Java虛擬機中運行時提供有關基于Java技術的應用程序的詳細信息。提供內存和CPU分析、堆轉儲分析、內存泄漏檢測、MBean訪問和垃圾收集。 |
- WebService工具:與CORBA一起在JDK 11中被移除
| schemagen | 用于 XML 綁定的Schema 生成器,用于生成 XML Schema文件 |
| wsgen | XML Web Service 2.0 的Java API,生成用于JAX-WS Web Service 的JAX-WS便攜式產物 |
| wsimport | XML Web Service 2.0 的Java API,生成用于根據服務端發布的WSDL文件生成客戶端 |
| xjc | 主要用于根據XML Schema 文件生成對應的Java類 |
- REPL和腳本工具
| jshell | 基于Java的 Shell REPL(Read-Eval-Print Loop)交互工具 |
| jjs | 對Nashorn 引擎的調用入口。Nashorn是基于Java實現的一個輕量級高性能JavaScript運行環境 |
| jrunscript | Java命令行腳本外殼工具(Conmmand Line Script Shell),主要用于解釋執行javas、Groovy、Ruby等腳本語言 |
總結
以上是生活随笔為你收集整理的第4章 虚拟机性能监控、故障处理工具(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python小白学习第二天
- 下一篇: 视觉技术再赋能,深眸科技一体化解决方案引