JVM中jhsdb除錯教程

banq發表於2024-04-03

jhsdb是JDK9中一個相對未被開發但非常強大的工具,用於除錯 JVM 問題。無論您是在處理導致 JVM 崩潰的本機程式碼,還是深入研究複雜的效能分析,瞭解如何jhsdb有效使用都可以成為您除錯工具庫中的遊戲規則改變者。

什麼jhsdb?
Java 9 引入了許多變化,其中模組是亮點。然而,在這些重大轉變之中,jhsdb並沒有得到應有的重視。 Oracle 官方將其描述jhsdb為可服務性代理工具,它是 JDK 的一部分,旨在進行快照除錯、效能分析,並提供對 Hotspot JVM 和在其上執行的 Java 應用程式的深入瞭解。簡而言之,jhsdb能深入 JVM 內部結構、瞭解核心轉儲以及診斷 JVM 或本機庫故障的首選工具。

jhsdb 入門
首先我們可以呼叫:

$ jhsdb --help
clhsdb           command line debugger
hsdb             ui debugger
debugd --help    to get more information
jstack --help    to get more information
jmap   --help    to get more information
jinfo  --help    to get more information
jsnap  --help    to get more information


該命令顯示jhsdb包含六個不同的工具:

  1. debugd:用於遠端連線和診斷的遠端除錯伺服器。
  2. jstack:提供詳細的堆疊和鎖資訊。
  3. jmap:提供對堆記憶體的深入瞭解。
  4. jinfo:顯示 JVM 基本資訊。
  5. jsnap:協助效能資料。
  6. Command Line Debugger命令列偵錯程式:儘管人們更喜歡 GUI,但我們將重點關注 GUI 除錯,以獲得更直觀的方法。

理解和使用debugd
debugd由於其遠端除錯性質,可能不是您生產環境的首選。然而,它對於本地容器除錯可能很有價值。
要使用它,我們首先需要檢測 JVM 程序 ID (PID),我們可以使用jps命令來完成此操作。
不幸的是,由於 UI 中的錯誤,您當前無法透過 GUI 偵錯程式連線到遠端伺服器。
我只能將其與命令列工具一起使用,例如jstack(下面討論)。
jhsdb debugd --pid 1234

我們可以連線到程序 1234。然後我們可以使用類似的工具jstack來獲取附加資訊:
jhsdb jstack --connect localhost

--connect引數適用於全域性並且應該適用於所有命令。

利用 jstack 進行執行緒轉儲
jstack有助於生成執行緒轉儲,這對於分析使用者計算機或生產環境中的堆疊程序至關重要。該命令可以顯示詳細的 JVM 執行狀態,包括死鎖檢測、執行緒狀態和編譯見解。

通常我們會jstack在本地使用,這樣就不需要debugd:

$ jhsdb jstack --pid 1234
Attaching to process ID 1234, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.13+8-LTS
Deadlock Detection:

No deadlocks found.

<font>"Keep-Alive-Timer" #189 daemon prio=8 tid=0x000000011d81f000 nid=0x881f waiting on condition [0x0000000172442000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
   JavaThread state: _thread_blocked
 - java.lang.Thread.sleep(long) @bci=0 (Interpreted frame)
 - sun.net.www.http.KeepAliveCache.run() @bci=3, line=168 (Interpreted frame)
 - java.lang.Thread.run() @bci=11, line=829 (Interpreted frame)
 - jdk.internal.misc.InnocuousThread.run() @bci=20, line=134 (Interpreted frame)


"DestroyJavaVM" #171 prio=5 tid=0x000000011f809000 nid=0x2703 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   JavaThread state: _thread_blocked

此快照可以幫助我們推斷有關應用程式在本地和生產中如何執行的許多細節。

  • 我們的程式碼編譯了嗎?
  • 它正在等待監視器嗎?
  • 還有哪些其他執行緒正在執行以及它們在做什麼?

使用 jmap 進行堆記憶體分析
對於深入研究 RAM 和堆記憶體來說,jmap這是無與倫比的。它顯示全面的堆記憶體詳細資訊,有助於 GC 調整和效能最佳化。特別有用的是histo透過 RAM 使用直方圖識別潛在記憶體洩漏的標誌。

jstackjmap 的典型用法與本文中提到的其他工具非常相似:

$ jhsdb jmap --pid 1234 --heap
Attaching to process ID 1234, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.13+8-LTS

using thread-local object allocation.
Garbage-First (G1) GC with 9 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 17179869184 (16384.0MB)
   NewSize                  = 1363144 (1.2999954223632812MB)
   MaxNewSize               = 10305404928 (9828.0MB)
   OldSize                  = 5452592 (5.1999969482421875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 4194304 (4.0MB)

Heap Usage:
G1 Heap:
   regions  = 4096
   capacity = 17179869184 (16384.0MB)
   used     = 323663048 (308.6691360473633MB)
   free     = 16856206136 (16075.330863952637MB)
   1.8839668948203325% used
G1 Young Generation:
Eden Space:
   regions  = 66
   capacity = 780140544 (744.0MB)
   used     = 276824064 (264.0MB)
   free     = 503316480 (480.0MB)
   35.483870967741936% used
Survivor Space:
   regions  = 8
   capacity = 33554432 (32.0MB)
   used     = 33554432 (32.0MB)
   free     = 0 (0.0MB)
   100.0% used
G1 Old Generation:
   regions  = 4
   capacity = 478150656 (456.0MB)
   used     = 13284552 (12.669136047363281MB)
   free     = 464866104 (443.3308639526367MB)
   2.7783193086322986% used

在大多數情況下,這可能看起來像是胡言亂語,但當我們經歷 GC 崩潰時,這可能是你武器庫中的秘密武器。您可以使用它來微調 GC 設定並確定要設定的正確引數。由於這可以輕鬆地在生產中執行,因此您可以基於現實世界的觀察。

如果您可以重現記憶體洩漏但沒有附加偵錯程式,則可以使用它來生成記憶體直方圖:

$ jhsdb jmap --pid 1234 --histo
Attaching to process ID 72640, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.13+8-LTS
Iterating over heap. This may take a while...
Object Histogram:

num       instances    bytes    Class description
--------------------------------------------------------------------------
1:        225689    204096416    int[]
2:        485992    59393024    byte[]
3:        17221    23558328    sun.security.ssl.CipherSuite[]
4:        341376    10924032    java.util.HashMap$Node
5:        117706    9549752    java.util.HashMap$Node[]
6:        306720    7361280    java.lang.String
7:        12718    6713944    char[]
8:        113884    5466432    java.util.HashMap
9:        64683    4657176    java.util.regex.Matcher
10:        95612    4615720    java.lang.Object[]
11:        106233    4249320    java.util.HashMap$KeyIterator
12:        16166    4090488    long[]
13:        126977    4063264    java.util.concurrent.ConcurrentHashMap$Node
14:        150789    3618936    java.util.ArrayList
15:        130167    3546016    java.lang.String[]
16:        156237    3227152    java.lang.Class[]
17:        33145    2916760    java.lang.reflect.Method
18:        32193    2575440    nonapi.io.github.classgraph.fastzipfilereader.FastZipEntry
19:        17314    2051672    java.lang.Class
20:        32043    1794408    io.github.classgraph.ClasspathElementZip$1
21:        107918    1726688    java.util.HashSet
22:        105970    1695520    java.util.HashMap$KeySet

這可以幫助縮小問題的根源。在 IDE 和開發過程中,有更好的工具可以實現這一點。但如果您在本地執行伺服器,它可以立即為您提供 RAM 快照。

jinfo 的基本 JVM 見解
雖然不像其他命令那麼詳細,但jinfo對於快速瀏覽系統屬性和 JVM 標誌非常有用,尤其是在不熟悉的機器上。這是一個簡單的工具,只需要 PID 即可執行。

jhsdb jinfo --pid 1234

jsnap 的效能指標
jsnap提供豐富的內部指標和統計資料,例如執行緒數和峰值數。這些資料對於微調執行緒池大小等方面至關重要,直接影響生產開銷。

$ jhsdb jsnap --pid 72640
Attaching to process ID 72640, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.13+8-LTS
java.threads.started=418 event(s)
java.threads.live=12
java.threads.livePeak=30
java.threads.daemon=8
java.cls.loadedClasses=16108 event(s)
java.cls.unloadedClasses=0 event(s)
java.cls.sharedLoadedClasses=0 event(s)
java.cls.sharedUnloadedClasses=0 event(s)
java.ci.totalTime=23090159603 tick(s)
java.property.java.vm.specification.version=11
java.property.java.vm.specification.name=Java Virtual Machine Specification
java.property.java.vm.specification.vendor=Oracle Corporation
java.property.java.vm.version=11.0.13+8-LTS
java.property.java.vm.name=OpenJDK 64-Bit Server VM
java.property.java.vm.vendor=Azul Systems, Inc.
java.property.java.vm.info=mixed mode
java.property.jdk.debug=release

GUI 除錯:視覺化方法
我們將跳過 CLI 偵錯程式,GUI 偵錯程式值得一提的是其使用者友好的介面,允許輕鬆連線到核心檔案、伺服器或 PID。這個視覺化工具開闢了除錯的新維度,特別是在使用 JNI 本機程式碼時。

GUI 偵錯程式可以像任何其他工具一樣啟動
jhsdb hsdb --pid 1234

GUI 佈局旨在簡化導航,提供 JVM 內部結構的全面檢視,一目瞭然。以下是一些主要功能以及如何使用它們:

  • 檔案選單File Menu:這是連線到除錯目標的起點。您可以載入核心檔案以進行事後分析,附加到正在執行的程序以診斷實時問題,或者如果您正在處理分散式系統,則可以連線到遠端除錯伺服器。
  • 執行緒和監視器Threads and Monitors:GUI 提供了執行緒狀態的實時檢視,從而更容易識別死鎖、執行緒爭用和監視器鎖。這種視覺化表示簡化了查明可能影響應用程式效能的併發問題的過程。
  • 堆摘要Heap Summary:對於記憶體分析,GUI 偵錯程式提供堆使用情況的圖形概述,包括生成(用於 GC 分析)、物件計數和記憶體佔用。這使得識別記憶體洩漏和最佳化垃圾收集策略更加直觀。
  • 方法和堆疊檢查Method and Stack Inspection:無縫地深入研究方法執行和堆疊幀,允許您跟蹤執行路徑、檢查區域性變數並評估不同時間點的應用程式狀態。

總結
jhsdb 是除錯工具包中不可或缺的工具,對於處理 JVM 和原生代碼問題的人來說尤其如此。從深度記憶體分析到效能指標,jhsdb 功能豐富,是開發人員和系統管理員的不二之選。

它的最大優勢在於除錯 Java 程式碼與原生代碼之間的互動。這類程式碼經常以奇怪的方式在終端使用者機器上出現故障。在這種情況下,典型的偵錯程式可能不是最好的工具,也可能無法揭示全貌。尤其是在獲得 JVM 核心轉儲(這正是 jhsdb 的主要用例)時,情況更是如此。
 

相關文章