天天看點

使用JDK中自帶的JVM分析工具解決記憶體溢出問題

作者:JAVA後端架構
使用JDK中自帶的JVM分析工具解決記憶體溢出問題
記憶體溢出,妥妥的名場面;

一、業務背景

對于分布式架構中的檔案服務來說,由于涉及大量的IO流操作,很容易引發JVM的相關異常,尤其是記憶體溢出的問題;

使用JDK中自帶的JVM分析工具解決記憶體溢出問題

在最近的一次版本疊代中,真實的業務處理場景和上述幾乎一緻,由于在檔案服務中添加批量處理的動作,直接喚醒了隐藏許久的BUG,就是最常見的記憶體溢出;

問題的起因:在word文檔完成内容識别後,轉換為pdf檔案,然後進行頁面分割轉為一組圖檔,在這個複雜并且超長的流程中存在一個數組容器未銷毀;

解決的方式:分析JVM的dump檔案,定位OOM問題引發的根本原因,結合檔案服務的異常日志分析,添加資源的釋放動作,進而解決問題;

二、Jdk-Bin目錄

對于相當一部分新手來說,看到JVM的問題都是Bug不知所起一臉懵的,其實這種心态大可不必,從職場幾年的開發經驗上看,JVM的問題大緻分為兩種:

  • 開發輕松解決:可以更新記憶體資源或者調整配置設定,又或者對程式優化,完成相關資源的管理和釋放,這是最常用的手段;
  • 輕松解決開發:由于經驗不足,程式出現重大BUG導緻JVM異常,進而引起系列的連鎖反應,這種不會絕地反彈,隻有一地雞毛;

在解決正常的JVM異常時,通常依賴JDK中基礎工具即可完成問題的定位,進而進行分析和解決,不過這些需要對基礎工具熟練使用才行,而很多JDK自身的能力又是經常被忽略的;

在jdk的bin目錄中,有很多自帶工具可以用于對JVM的分析;

使用JDK中自帶的JVM分析工具解決記憶體溢出問題

上述是基于jdk1.8的目錄,裡面有很多開發經常用到指令,下面圍繞一個微服務的啟動和運作,來看看基于JDK中自帶JVM工具的用法;

三、指令行工具

1、jps指令

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
           

2、jinfo指令

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】參數
           

3、jstat指令

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
  }
  ......
}
           

4、jstack指令

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.
           

5、jmap指令

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
           

四、可視化工具

1、jconsole

Java内置的JVM性能監控工具,在熟悉上述的指令行工具之後,對于該可視化工具的使用不會太陌生,在指令中可以檢視到的預設參數或者應用自定義配置,在該工具中也可以找到,并且以圖形化的方式呈現;

$ jconsole  # 通過該指令會喚起jconsole界面
           
使用JDK中自帶的JVM分析工具解決記憶體溢出問題

這裡選擇線程一欄,可以直覺的看到線程數量的變化曲線,也可以在下方檢視某個線程的具體資訊,并且可以通過檢測死鎖功能,發現在服務中建立的test-thread-01和test-thread-02兩條線程;

使用JDK中自帶的JVM分析工具解決記憶體溢出問題

2、visualvm

VisualVM作為解決JVM問題的常用工具,內建的功能豐富且強大,此處通過Idea插件的方式啟動FacadeApp微服務,在程序首頁可以看到自定義的配置,線程一欄因為檢測到死鎖直接給到異常提示;

使用JDK中自帶的JVM分析工具解決記憶體溢出問題

在監視一欄可以通過【堆dump】檢視詳細的資訊,可以檢視類的執行個體數和大小,并且完成了結果排序和占用統計;此處資訊在定位和解決JVM問題時非常重要;

使用JDK中自帶的JVM分析工具解決記憶體溢出問題

對于JVM的監控工具來說,其能力與常用的指令行文法差異很小,并且這些指令在jdk中tools.jar包也可以找到其對應的類,對于一些更進階的監控平台來說,例如Kuboard、Skywalking等,也對這些底層能力做了內建,其原理應該也是大同小異。

為幫助開發者們提升面試技能、有機會入職BATJ等大廠公司,特别制作了這個專輯——這一次整體放出。

大緻内容包括了: Java 集合、JVM、多線程、并發程式設計、設計模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat等大廠面試題等、等技術棧!

使用JDK中自帶的JVM分析工具解決記憶體溢出問題

歡迎大家關注公衆号【Java爛豬皮】,回複【666】,擷取以上最新Java後端架構VIP學習資料以及視訊學習教程,然後一起學習,一文在手,面試我有。

每一個專欄都是大家非常關心,和非常有價值的話題,如果我的文章對你有所幫助,還請幫忙點贊、好評、轉發一下,你的支援會激勵我輸出更高品質的文章,非常感謝!

使用JDK中自帶的JVM分析工具解決記憶體溢出問題

繼續閱讀