[轉帖]JVM記憶體配置最佳實踐

济南小老虎發表於2024-05-18

https://help.aliyun.com/zh/sae/use-cases/best-practices-for-jvm-heap-size-configuration

如果JVM堆空間大小設定過大,可能會導致Linux系統的OOM Killer被啟用,進而結束(kill)Java應用程序,在容器環境下可能會表現為頻繁異常重啟。本文介紹在容器環境下JVM堆引數的配置建議,以及OOM的相關常見問題。

透過-XX:MaxRAMPercentage限制堆大小(推薦)

  • 在容器環境下,Java只能獲取伺服器的配置,無法感知容器記憶體限制。您可以透過設定-Xmx來限制JVM堆大小,但該方式存在以下問題:

    • 當規格大小調整後,需要重新設定堆大小引數。

    • 當引數設定不合理時,會出現應用堆大小未達到閾值但容器OOM被強制關閉的情況。

      說明

      應用程式出現OOM問題時,會觸發Linux核心的OOM Killer機制。該機制能夠監控佔用過大記憶體,尤其是瞬間消耗大量記憶體的程序,然後它會強制關閉某項程序以騰出記憶體留給系統,避免系統立刻崩潰。

  • 推薦的JVM引數設定。

    -XX:+UseContainerSupport -XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/admin/nas/gc-${POD_IP}-$(date '+%s').log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/nas/dump-${POD_IP}-$(date '+%s').hprof

    引數說明如下。

    引數

    說明

    -XX:+UseContainerSupport

    使用容器記憶體。允許JVM從主機讀取cgroup限制,例如可用的CPU和RAM,並進行相應的配置。當容器超過記憶體限制時,會丟擲OOM異常,而不是強制關閉容器。

    -XX:InitialRAMPercentage

    設定JVM使用容器記憶體的初始百分比。建議與-XX:MaxRAMPercentage保持一致,推薦設定為70.0。

    -XX:MaxRAMPercentage

    設定JVM使用容器記憶體的最大百分比。由於存在系統元件開銷,建議最大不超過75.0,推薦設定為70.0。

    -XX:+PrintGCDetails

    輸出GC詳細資訊。

    -XX:+PrintGCDateStamps

    輸出GC時間戳。日期形式,例如2019-12-24T21:53:59.234+0800。

    -Xloggc:/home/admin/nas/gc-${POD_IP}-$(date '+%s').log

    GC日誌檔案路徑。需保證Log檔案所在容器路徑已存在,建議您將該容器路徑掛載到NAS目錄或收集到SLS,以便自動建立目錄以及實現日誌的持久化儲存。

    -XX:+HeapDumpOnOutOfMemoryError

    JVM發生OOM時,自動生成Dump檔案。

    -XX:HeapDumpPath=/home/admin/nas/dump-${POD_IP}-$(date '+%s').hprof

    Dump檔案路徑。需保證Dump檔案所在容器路徑已存在,建議您將該容器路徑掛載到NAS目錄,以便自動建立目錄以及實現日誌的持久化儲存。

    說明
    • 使用-XX:+UseContainerSupport引數需JDK 8u191+、JDK 10及以上版本。

    • JDK 11版本下日誌相關的引數-XX:+PrintGCDetails-XX:+PrintGCDateStamps-Xloggc:$LOG_PATH/gc.log引數已廢棄,請使用引數-Xlog:gc:$LOG_PATH/gc.log代替。

    • Dragonwell 11不支援${POD_IP}變數。

    • 如果您沒有將/home/admin/nas容器路徑掛載到NAS目錄,則必須保證該目錄在應用啟動前已存在,否則將不會產生日誌檔案。

透過-Xms -Xmx限制堆大小

  • 您可以透過設定-Xms-Xmx來限制堆大小,但該方式存在以下兩個問題:

    • 當規格大小調整後,需要重新設定堆大小引數。

    • 當引數設定不合理時,會出現應用堆大小未達到閾值但容器OOM被強制關閉的情況。

      說明

      應用程式出現OOM問題時,會觸發Linux核心的OOM Killer機制。該機制能夠監控佔用過大記憶體,尤其是瞬間消耗大量記憶體的程序,然後它會強制關閉某項程序以騰出記憶體留給系統,避免系統立刻崩潰。

  • 推薦的JVM引數設定。

    -Xms2048m -Xmx2048m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/admin/nas/gc-${POD_IP}-$(date '+%s').log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/nas/dump-${POD_IP}-$(date '+%s').hprof

    引數說明如下。

    引數

    說明

    -Xms

    設定JVM初始記憶體大小。建議與-Xmx相同,避免每次垃圾回收完成後JVM重新分配記憶體。

    -Xmx

    設定JVM最大可用記憶體大小。為避免容器OOM,請為系統預留足夠的記憶體大小。

    -XX:+PrintGCDetails

    輸出GC詳細資訊。

    -XX:+PrintGCDateStamps

    輸出GC時間戳。日期形式,例如2019-12-24T21:53:59.234+0800。

    -Xloggc:/home/admin/nas/gc-${POD_IP}-$(date '+%s').log

    GC日誌檔案路徑。需保證Log檔案所在容器路徑已存在,建議您將該容器路徑掛載到NAS目錄或收集到SLS,以便自動建立目錄以及實現日誌的持久化儲存。

    -XX:+HeapDumpOnOutOfMemoryError

    JVM發生OOM時,自動生成Dump檔案。

    -XX:HeapDumpPath=/home/admin/nas/dump-${POD_IP}-$(date '+%s').hprof

    Dump檔案路徑。需保證Dump檔案所在容器路徑已存在,建議您將該容器路徑掛載到NAS目錄,以便自動建立目錄以及實現日誌的持久化儲存。

  • 推薦的堆大小設定。

    記憶體規格大小

    JVM堆大小

    1 GB

    600 MB

    2 GB

    1434 MB

    4 GB

    2867 MB

    8 GB

    5734 MB

透過ossutil下載堆轉儲檔案

  1. 掛載容器日誌目錄至NAS。具體操作,請參見設定NAS儲存

  2. 設定JVM引數。

    其中Dump檔案路徑/home/admin/nas為NAS掛載目錄:

    -Xms2048m -Xmx2048m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/admin/nas/gc-${POD_IP}-$(date '+%s').log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/nas/dump-${POD_IP}-$(date '+%s').hprof
  3. 當應用發生OOM時,會生成堆轉儲檔案到NAS掛載目錄,您可以利用ossutil工具,將該Dump檔案下載到本地進行分析。具體操作,請參見透過日誌上傳下載診斷應用

常見問題

容器出現137退出碼的含義是什麼?

當容器使用記憶體超過限制時,會出現容器OOM,導致容器被強制關閉。此時業務應用記憶體可能並未達到JVM堆大小上限,所以不會產生Dump日誌。建議您調小JVM堆大小的上限,為容器內其他系統元件預留足夠多的記憶體空間。m_exitcode_137

為什麼發生OOM卻沒有生成Dump檔案?

當發生OOM Killer時,並不一定會發生JVM OOM,所以不會生成Dump檔案。您可以採取以下方式來避免這種情況。

  • 如果是Java應用,可以適當調小JVM的堆記憶體大小。具體配置,請參見本文。

  • 如果是非Java應用,可以調整例項規格,保證充裕的記憶體資源。具體配置,請參見變更例項規格

堆大小和規格記憶體的引數值可以相同嗎?

不可以。因為系統自身元件存在記憶體開銷,例如使用SLS進行日誌收集(設定日誌收集至SLS)時會佔用一小部分的記憶體空間,所以不能將JVM堆大小設定為和規格記憶體大小相同的數值,需要為這些系統元件預留足夠的記憶體空間。

在JDK 8版本下設定-XX:MaxRAMPercentage值為整數時報錯怎麼處理?

這是JDK 8的一個Bug。具體資訊,請參見Java Bug Database。例如,在JDK 8u191版本下,設定-XX:MaxRAMPercentage=70,此時JVM會啟動報錯。m_JDK8_bug

解決方案如下:

  • 方式一:設定-XX:MaxRAMPercentage70.0

    說明

    如果您使用了-XX:InitialRAMPercentage-XX:MinRAMPercentage,引數值同樣不可設定為整數,需按照方式一的形式來設定。

  • 方式二:升級JDK版本至JDK 10及以上版本。

為什麼JVM引數設定了6 GB,但是記憶體使用率卻很低?

雖然JVM引數已設定-Xms6g -Xmx6g,但是作業系統不會馬上分配6 GB的實體記憶體,需要實際使用後才分配。因此,記憶體使用率在應用啟動的時候,會相對較低,後續會出現攀爬現象。

相關文件

文件

說明

JVM引數配置說明

介紹典型的JVM配置引數,例如調優堆疊記憶體與調優回收器GC。

設定啟動命令

如果在啟動前需要進行特殊配置,或者不採用預設的啟動引數,可以透過SAE設定容器啟動和執行時所需要的命令。

相關文章