監控 JVM 的非堆疊記憶體使用情況
1. 概述
使用 Java 應用程式時,我們有時會遇到記憶體消耗的問題。總的來說,我們可以將所有問題分為基於堆記憶體使用情況的問題和基於非堆記憶體使用情況的問題。
在本文中,我們將回顧可用於監控非堆記憶體利用率的不同工具。
2. jcmd
jcmd 是一個 JDK 工具,我們可以使用它來向正在執行的 Java 程序發送診斷和故障排除命令。我們利用它來檢查系統屬性,觸發垃圾收集,轉儲堆和線程信息,並管理 JIT 編譯。
要使用 jcmd 監控非堆內存,我們需要啟用本機記憶體追蹤。然後我們可以呼叫jcmd
來取得記憶體進程資訊:
jcmd <pid> VM.native_memory
在回應中,我們可以分析我們需要的所有非堆疊部分:
- GC (reserved=359872KB, committed=70440KB)
(malloc=17792KB #582)
(mmap: reserved=342080KB, committed=52648KB)
- Compiler (reserved=168KB, committed=168KB)
(malloc=4KB #29)
(arena=165KB #5)
- Internal (reserved=221KB, committed=221KB)
(malloc=157KB #1202)
(mmap: reserved=64KB, committed=64KB)
- Other (reserved=4KB, committed=4KB)
(malloc=4KB #2)
- Symbol (reserved=1217KB, committed=1217KB)
(malloc=857KB #3546)
(arena=360KB #1)
- Native Memory Tracking (reserved=141KB, committed=141KB)
(malloc=5KB #69)
(tracking overhead=136KB)
//shorten
在輸出中,我們可以看到堆記憶體消耗以及所有非堆記憶體類型的消耗。 committed
屬性表示jcmd
調用時的消耗。
3. jconsole
jconsole是一個遵守JMX規範的監控工具。當我們只能存取 JDK 時,我們可以使用 jconsole 來即時監控和管理 Java 應用程式。
Jconsole 提供了一個圖形介面來追蹤記憶體使用情況、線程活動、CPU 消耗和 MBean 。該工具連接到本地或遠端 JVM,幫助我們診斷效能問題並優化應用程式行為。
要執行 jconsole,我們從 JDK bin
資料夾執行以下命令:
<JDK_PATH>\bin\jconsole
在第一個畫面上,我們選擇想要連線的本機或遠端進程:
在Memory
標籤上,我們選擇Non-Heap Memory
Usage
」圖表。之後,我們可以看到非堆使用情況的圖表:
4. VisualVM
VisualVM 是另一個提供 JVM 監控功能的視覺化工具。我們使用 VisualVM 來監控 CPU 負載、記憶體使用情況、執行緒活動和堆轉儲。 VisualVM 也支援分析執行緒轉儲和分析方法執行以識別效能瓶頸。
4.1.在基本監控中監控 MetaSpace
在基本的 VisualVM 設定中,我們可以使用Monitor
標籤來監視元空間大小:
為了監控其他非堆疊部分,我們可以安裝額外的VisualVM插件。
4.2.使用 Mbeans 插件監控非堆內存
透過新增 MBeans VisualVM 插件,我們將看到新的MBeans
選項卡。在此標籤上,我們可以查看目前非堆記憶體的使用情況,尤其是特定記憶體部分的使用情況:
4.3.使用緩衝池插件監控直接緩衝區
此外,我們可以使用 Buffer Monitor VisualVM 插件。安裝完成後,我們將看到新的Buffer Pools
選項卡,在這裡我們可以查看當前直接緩衝記憶體和記憶體映射檔案的使用情況:
5. Java 任務控制
Java Mission Control是另一個提供 JVM 監控和診斷功能的視覺化工具。我們利用 JMC 來分析應用程式效能、監控記憶體使用情況、檢查線程活動並檢查垃圾收集行為。
JMC 還支援分析方法執行和分析飛行記錄以識別效能瓶頸。
5.1.使用 MBean 瀏覽器的非堆記憶體使用情況
讓我們使用MBean Server
連接到我們的應用程式。在MBeans Browser
標籤上,我們可以找到非堆疊記憶體的總利用率。但是,我們應該知道這個數字不包括直接緩衝記憶體消耗:
如果我們選擇BufferPool
組下的部分,我們可以找到直接池記憶體的使用:
5.2.使用 JFR 記錄進行本機記憶體跟踪
自 Java 20 起,我們也能夠使用 JFR 持續記錄本機記憶體追蹤資料。使用下一個命令,我們可以準備一個飛行記錄器檔案並追蹤所需的事件:
java -XX:NativeMemoryTracking=detail -XX:StartFlightRecording=name=Profiling,filename=nmt-recording.jfr,settings=profile -jar path/ourapp.jar
然後,我們可以使用 Java Mission Control 開啟此檔案:
在記錄的Event Browser
標籤上,我們可以找到Event Types Tree
。在其中,我們可以找到Total Native Memory Usage
事件。如果我們選擇它,螢幕右側將顯示已提交的記憶體總量。這個數字包括堆和非堆記憶體消耗。
此外,我們可以選擇Native Memory Usage Per Type
事件。在這種情況下,我們看到所有非堆記憶體類型的消耗細目:
6. JMX-DExporter
JMX-DExporter是一種以與 Prometheus 相容的格式公開 JMX 指標的工具。我們可以使用 JMX-DExporter 收集 JVM 指標,例如記憶體使用情況、線程活動、垃圾收集統計資料等。
6.1.指標端點使用情況
要開始使用 JMX-DExporter,首先我們需要下載代理 JAR 。然後,我們使用jmx_exporter_config.yml
來設定我們的導出器:
startDelaySeconds: 0
lowercaseOutputName: true
lowercaseOutputLabelNames: true
rules:
- pattern: "java.lang:type=Memory"
name: "jvm_memory_usage_bytes"
labels:
area: "$2"
type: GAUGE
在這個例子中,我們配置了java.lang:type=Memory
模式。這意味著我們要將MemoryMXBean資料匯出到我們的指標端點。 現在,我們能夠啟動附加了 JMX-DExporter 的應用程式:
java -javaagent:.path-to-agent-jar\jmx_prometheus_javaagent.jar=port:path-to-agent-jar\jmx_exporter_config.yml -jar .path-to-app\app.jar
在啟動命令中,我們指定下載的代理 JAR 的路徑以及新指標端點可用的連接埠。為此,我們使用javaagent
參數。此外,我們還指定了設定檔和應用程式的 JAR 檔案的路徑。
現在,如果我們呼叫http://localhost:port/metrics
端點,我們會看到記憶體 MBean 提供的完整信息,包括非堆記憶體使用量資料:
//shorten
jvm_memory_committed_bytes{area="nonheap"} 1.8546688E7
# HELP jvm_memory_init_bytes Initial bytes of a given JVM memory area.
# TYPE jvm_memory_init_bytes gauge
jvm_memory_init_bytes{area="heap"} 5.32676608E8
jvm_memory_init_bytes{area="nonheap"} 7667712.0
# HELP jvm_memory_max_bytes Max (bytes) of a given JVM memory area.
# TYPE jvm_memory_max_bytes gauge
jvm_memory_max_bytes{area="heap"} 8.518631424E9
jvm_memory_max_bytes{area="nonheap"} -1.0
//shorten
6.2.與 Prometheus 集成
Prometheus是一個開源監控和警報工具包,我們用它來收集和儲存指標作為時間序列資料。我們可以輕鬆地將我們的指標端點與 Prometheus 整合。
安裝 Prometheus 實例後,我們使用以下值修改prometheus.yml
檔案:
scrape_configs:
- job_name: "our_app"
static_configs:
- targets: ["localhost:port"]
在job_name
屬性中,我們指定 Prometheus 目標的命名方式。在targets
屬性中,我們指定指標端點的主機和連接埠。
完成配置變更後,我們需要重新啟動 Prometheus 實例並開啟控制台。在輸入欄位中,我們可以輸入任何可用的指標,包括非堆疊使用情況。此後,內存指標圖表將顯示在螢幕上:
7. 結論
在本教程中,我們回顧了一組幫助我們監控非堆記憶體使用情況的工具。
對於簡單的情況,當我們不需要詳細的報表並且可以存取 JDK 時,我們可以考慮直接使用jcmd
或jconsole
等基本工具。對於更複雜的情況,需要詳細了解特定的記憶類型,我們可以使用飛行記錄和 JMC。
為了將我們的監控整合到 Prometheus 指標中,我們可以使用 JMX-Exporter 代理。在每種特定情況下都應考慮所有監控工具的額外資源消耗。