當前位置: 妍妍網 > 碼農

阿裏來的同事,解決記憶體溢位,真優雅!

2024-05-24碼農

大家好,我是磊哥。

記憶體又溢位了,來看看阿裏同學,如何使用JDK內建工具進行分析排查的吧...

一、業務背景

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

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

問題的起因:在word文件完成內容辨識後,轉換為pdf檔,然後進行頁面分割轉為一組圖片,在這個復雜並且超長的流程中存在一個陣列容器未銷毀;

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

二、Jdk-Bin目錄

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

  • 開發輕松解決:可以升級記憶體資源或者調整分配,又或者對程式最佳化,完成相關資源的管理和釋放,這是最常用的手段;

  • 輕松解決開發:由於經驗不足,程式出現重大BUG導致JVM異常,進而引起系列的連鎖反應,這種不會絕地反彈,只有一地雞毛;

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

    如果你近期準備面試跳槽,建議在ddkk.com線上刷題,涵蓋 一萬+ 道 Java 面試題,幾乎覆蓋了所有主流技術面試題,還有市面上最全的技術五百套,精品系列教程,免費提供。

    在jdk的bin目錄中,有很多內建工具可以用於對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 waitfor <0x00000007b00a35d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
    "test-thread-01":
    at sun.misc.Unsafe.park(Native Method)
    - parking to waitfor <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效能監控工具,在熟悉上述的命令列工具之後,對於該視覺化工具的使用不會太陌生,在命令中可以檢視到的預設參數或者套用自訂配置,在該工具中也可以找到,並且以圖形化的方式呈現;

    如果你近期準備面試跳槽,建議在ddkk.com線上刷題,涵蓋 一萬+ 道 Java 面試題,幾乎覆蓋了所有主流技術面試題,還有市面上最全的技術五百套,精品系列教程,免費提供。

    $ jconsole # 透過該命令會喚起jconsole界面

    這裏選擇執行緒一欄,可以直觀的看到執行緒數量的變化曲線,也可以在下方檢視某個執行緒的具體資訊,並且可以透過檢測死結功能,發現在服務中建立的test-thread-01和test-thread-02兩條執行緒;

    2、visualvm

    VisualVM作為解決JVM問題的常用工具,整合的功能豐富且強大,此處透過Idea外掛程式的方式啟動FacadeApp微服務,在行程主頁可以看到自訂的配置,執行緒一欄因為檢測到死結直接給到異常提示;

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

    對於JVM的監控工具來說,其能力與常用的命令列語法差異很小,並且這些命令在jdk中 tools.jar 包也可以找到其對應的類,對於一些更高級的監控平台來說,例如Kuboard、Skywalking等,也對這些底層能力做了整合,其原理應該也是大同小異。

    🔥 磊哥私藏精品 熱門推薦 🔥