記憶體溢位,妥妥的名場面;
對於分散式架構中的檔案服務來說,由於涉及大量的IO流操作,很容易引發JVM的相關異常,尤其是記憶體溢位的問題;
在最近的一次版本迭代中,真實的業務處理場景和上述幾乎一致,由於在檔案服務中新增批次處理的動作,直接喚醒了隱藏許久的BUG,就是最常見的記憶體溢位;
問題的起因:在word檔案完成內容識別後,轉換為pdf檔案,然後進行頁面分割轉為一組圖片,在這個複雜並且超長的流程中存在一個陣列容器未銷燬;
解決的方式:分析JVM的dump檔案,定位OOM問題引發的根本原因,結合檔案服務的異常紀錄檔分析,新增資源的釋放動作,從而解決問題;
對於相當一部分新手來說,看到JVM的問題都是Bug不知所起一臉懵的,其實這種心態大可不必,從職場幾年的開發經驗上看,JVM的問題大致分為兩種:
在解決常規的JVM異常時,通常依賴JDK中基礎工具即可完成問題的定位,從而進行分析和解決,不過這些需要對基礎工具熟練使用才行,而很多JDK自身的能力又是經常被忽略的;
在jdk的bin目錄中,有很多自帶工具可以用於對JVM的分析;
上述是基於jdk1.8的目錄,裡面有很多開發經常用到命令,下面圍繞一個微服務的啟動和執行,來看看基於JDK中自帶JVM工具的用法;
jps:虛擬機器器程序狀態工具,該命令在Java環境部署和服務啟動檢視時經常用到,首先在本地啟動一個facade門面微服務,然後在命令列中執行查詢;
jps
:命令預設輸出的是程序ID和應用主類的名稱;-l
:輸出程序ID和應用主類的完整路徑;-v
:輸出向jvm傳遞的引數,此處展示為idea中顯式設定的VM-options引數,其他內容自行檢視即可;-m
:輸出向main方法傳遞的引數,服務啟動前可以在idea的Program-arguments設定;$ jps
1281 FacadeApp
$ jps -l
1281 com.explore.facade.FacadeApp
$ jps -v
1281 FacadeApp -Xms128m -Xmx256m -XX:MaxNewSize=256m -XX:MaxPermSize=256m
$ jps -m
1281 FacadeApp hello,main-method
jinfo:在命令後面帶pid程序號,可以輸出指定程序的設定資訊,在應用啟動時通常不會指定過多的設定引數,就可以使用該命令查詢很多引數的預設值;該命令還可以在執行時動態調整部分引數,只是很少被使用;
$ jinfo 1281 # 只貼上個別引數
Java System Properties: # 系統引數
java.runtime.version=1.8.0_144-b01
file.encoding=UTF-8
sun.java.command=com.explore.facade.FacadeApp hello,main-method
VM Flags: # 虛擬機器器引數
-XX:InitialHeapSize=134217728 -XX:MaxHeapSize=268435456 -XX:MaxNewSize=267911168
VM Arguments: # 執行時引數
jvm_args: -Xms128m -Xmx256m -XX:MaxNewSize=256m -XX:MaxPermSize=256m
java_command: com.explore.facade.FacadeApp hello,main-method
$ jinfo -sysprops 1281 # 只輸出【Java System Properties】引數
$ jinfo -flags 1281 # 只輸出【VM Flags】引數
jstat:以指定的頻率輸出JVM的監控指標,下述命令輸出記憶體佔用和GC相關資訊,每隔3秒輸出一次,連續列印5次;由於這裡只是啟動一個簡單的微服務,沒有執行業務邏輯,所以各項指標比較平穩;
$ jstat -gcutil 1281 3000 5
S0 S1 E O M CCS YGC YGCT FGC FGCT CGC CGCT GCT
0.00 0.00 57.97 64.16 92.82 88.75 3 0.028 9 0.516 - - 0.544
0.00 0.00 57.97 64.16 92.82 88.75 3 0.028 9 0.516 - - 0.544
該命令是比較常用的,這裡各項指標的統計邏輯,在tools.jar
包中有jstat_options
參考檔案,相對路徑sun/tools/jstat/resources/
目錄下;
option gcutil {
column {
header "^S0^" /* Survivor 0 Space - Percent Used */
data (1-((sun.gc.generation.0.space.1.capacity - sun.gc.generation.0.space.1.used)/sun.gc.generation.0.space.1.capacity)) * 100
}
column {
header "^S1^" /* Survivor 1 Space - Percent Used */
data (1-((sun.gc.generation.0.space.2.capacity - sun.gc.generation.0.space.2.used)/sun.gc.generation.0.space.2.capacity)) * 100
}
......
}
jstack:輸出指定程序當前時刻在JVM中的執行緒資訊,為了清楚的展示其效果,在服務啟動時建立執行緒死鎖,然後通過該命令就會把發生死鎖的執行緒列印出來,通過輸出可以發現兩條互相等待的執行緒資訊;
$ jstack 1281
Found one Java-level deadlock:
=============================
"test-thread-02":
waiting for ownable synchronizer 0x00000007b00a35d0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "test-thread-01"
"test-thread-01":
waiting for ownable synchronizer 0x00000007b00a35a0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "test-thread-02"
Java stack information for the threads listed above:
===================================================
"test-thread-02":
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007b00a35d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"test-thread-01":
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007b00a35a0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
Found 1 deadlock.
jmap:可以輸出指定程序的記憶體中物件對映資訊,或者堆的關鍵資訊、記憶體的使用統計、GC演演算法、設定、類的範例資訊及記憶體佔用等,該命令在解決JVM問題時也經常使用;
$ jmap 1281
$ jmap -heap 1281
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 536870912 (512.0MB)
Heap Usage:
PS Young Generation
Eden Space:
From Space:
To Space:
PS Old Generation
$ jmap -histo:live 1281
num #instances #bytes class name
----------------------------------------------
1311: 1 32 com.explore.facade.FacadeApp$$EnhancerBySpringCGLIB$$313d9e3
Java內建的JVM效能監控工具,在熟悉上述的命令列工具之後,對於該視覺化工具的使用不會太陌生,在命令中可以檢視到的預設引數或者應用自定義設定,在該工具中也可以找到,並且以圖形化的方式呈現;
$ jconsole # 通過該命令會喚起jconsole介面
這裡選擇執行緒一欄,可以直觀的看到執行緒數量的變化曲線,也可以在下方檢視某個執行緒的具體資訊,並且可以通過檢測死鎖功能,發現在服務中建立的test-thread-01和test-thread-02兩條執行緒;
VisualVM作為解決JVM問題的常用工具,整合的功能豐富且強大,此處通過Idea外掛的方式啟動FacadeApp微服務,在程序主頁可以看到自定義的設定,執行緒一欄因為檢測到死鎖直接給到異常提示;
在監視一欄可以通過【堆dump】檢視詳細的資訊,可以檢視類的範例數和大小,並且完成了結果排序和佔用統計;此處資訊在定位和解決JVM問題時非常重要;
對於JVM的監控工具來說,其能力與常用的命令列語法差異很小,並且這些命令在jdk中tools.jar
包也可以找到其對應的類,對於一些更高階的監控平臺來說,例如Kuboard、Skywalking等,也對這些底層能力做了整合,其原理應該也是大同小異。
程式設計檔案:
https://gitee.com/cicadasmile/butte-java-note
應用倉庫:
https://gitee.com/cicadasmile/butte-flyer-parent