容器化環境中,JVM最佳引數配置實踐

华为云开发者联盟發表於2024-02-29

本文分享自華為雲社群《Java應用容器化引數配置最佳實踐》,作者:可以交個朋友。

簡介

當你在物理機或者虛擬機器上配置 JVM 引數時,JVM會預設使用主機上1/4的記憶體作為堆記憶體,你也可以選擇使用-Xmx/-Xms 來指定 Java 堆記憶體大小。在容器化環境中,每個容器例項的記憶體大小由Cgroups配置決定,而低版本JVM對Cgroups的支援是不太友好的。本文主要探討JVM最佳引數配置

JDK與Cgroups的適配關係

JDK 1.8.0_131之前的版本

使用jdk 1.8.0_121版本映象啟動容器例項,不指定引數情況下,無法識別Cgroups記憶體限制,使用主機1/4的記憶體作為最大堆記憶體

[root@172 ~]# free -m
               total        used        free      shared  buff/cache   available
Mem:            7196        2557         299         117        4338        4219
Swap:              0           0           0
[root@172 ~]# docker run -m 512Mi openjdk:8u121 java  -XshowSettings:vm -version VM
VM settings:
    Max. Heap Size (Estimated): 1.56G
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_121"
OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-1~bpo8+1-b13)
OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)

使用-Xms-Xmx指定初始堆記憶體和最大堆記憶體,jvm能正常識別

[root@172 ~]# docker run -m 512Mi openjdk:8u121 java -Xms512m -Xmx512m -XshowSettings:vm -version VM
VM settings:
    Min. Heap Size: 512.00M
    Max. Heap Size: 512.00M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_121"
OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-1~bpo8+1-b13)
OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)

JDK 1.8.0_131版本

使用jdk 1.8.0_131版本映象啟動容器,不指定引數,無法識別Cgroups記憶體限制,使用主機1/4的記憶體作為最大堆記憶體

[root@172 ~]# docker run -m 512Mi openjdk:8u131 java  -XshowSettings:vm -version VM
VM settings:
    Max. Heap Size (Estimated): 1.56G
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-2-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)

使用-Xms和-Xmx指定初始堆記憶體和最大堆記憶體,jvm能正常識別

[root@172 ~]# docker run -m 512Mi openjdk:8u131 java -Xms512m -Xmx512m -XshowSettings:vm -version VM
VM settings:
    Min. Heap Size: 512.00M
    Max. Heap Size: 512.00M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-2-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)

在jdk 1.8.0_131版本開始,加入了兩個新引數-XX:+UnlockExperimentalVMOptions-XX:+UseCGroupMemoryLimitForHeap來動態感知容器的Cgroups記憶體限制,最大堆記憶體為Cgroups記憶體限制的1/4

[root@172 ~]# docker run -m 512Mi openjdk:8u131 java -XX:+UnlockExperimentalVMOptions  -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version VM
VM settings:
    Max. Heap Size (Estimated): 114.00M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-2-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)

新引數-XX:+UnlockExperimentalVMOptions-XX:+UseCGroupMemoryLimitForHeap雖然能動態感知Cgroups記憶體限制,但是卻只能使用1/4,無法修改。此時可以使用另外兩個引數-XX:MaxRAMFraction-XX:MinRAMFraction,引數值必須為整數,取值參考如下表格:

MaxRAMFraction/MinRAMFraction取值Cgroups記憶體限制的百分比
1 90%
2 50%
3 33%
4 25%
[root@172 ~]# docker run -m 512Mi openjdk:8u131 java -XX:+UnlockExperimentalVMOptions  -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=2 -XshowSettings:vm -version VM
VM settings:
    Max. Heap Size (Estimated): 228.00M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-2-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)

最大堆記憶體為Cgroups記憶體限制的50%,符合預期

JDK 1.8.0_191版本

使用jdk 1.8.0_191版本映象啟動容器,不指定引數,jvm能動態感知Cgroups記憶體限制,最大堆記憶體為Cgroups記憶體限制的1/4

[root@172 ~]# docker run -m 512Mi openjdk:8u191-alpine java  -XshowSettings:vm -version VM
VM settings:
    Max. Heap Size (Estimated): 123.75M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_191"
OpenJDK Runtime Environment (IcedTea 3.10.0) (Alpine 8.191.12-r0)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

使用-Xms-Xmx指定初始堆記憶體和最大堆記憶體,符合預期

[root@172 ~]# docker run -m 512Mi openjdk:8u191-alpine java -Xms512m -Xmx512m -XshowSettings:vm -version VM
VM settings:
    Min. Heap Size: 512.00M
    Max. Heap Size: 512.00M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_191"
OpenJDK Runtime Environment (IcedTea 3.10.0) (Alpine 8.191.12-r0)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

jdk 1.8.0_191版本開始,-XX:MaxRAMFraction-XX:MinRAMFraction被棄用,使用MaxRAMPercentageMinRAMPercentage來修改堆記憶體在Cgroups記憶體限制的佔比,引數值是Double型別必須帶小數點

[root@172 ~]# docker run -m 512Mi openjdk:8u191-alpine java -XX:MaxRAMPercentage=50.0 -XX:MinRAMPercentage=50.0 -XshowSettings:vm -version VM
VM settings:
    Max. Heap Size (Estimated): 247.50M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_191"
OpenJDK Runtime Environment (IcedTea 3.10.0) (Alpine 8.191.12-r0)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

總結

  • XmsXmx能適應所有JDK版本,但不能動態感知容器的Cgroups限制,且引數優先順序最高,與其他引數一起配置時,其他引數不生效。
  • -XX:+UnlockExperimentalVMOptions-XX:+UseCGroupMemoryLimitForHeap在1.8.0_131版本開始啟用,能動態感知容器的Cgroups限制,但最大堆記憶體只能使用容器Cgroups記憶體限制的1/4。
  • -XX:MaxRAMFraction-XX:MinRAMFraction在1.8.0_131版本開始啟用,可以修改堆記憶體佔容器Cgroups記憶體限制的百分比,但百分比的值不能自由指定(比如不能指定40%),在1.8.0_191版本開始棄用。
  • MaxRAMPercentageMinRAMPercentage在1.8.0_191版本開始啟用,可以自定義修改堆記憶體佔容器Cgroups記憶體限制的百分比。
以上僅適用於容器採用Cgroups v1版本,在Cgroups v2版本中jdk需要1.8.0_372、11.0.16及更高版本才能動態感知Cgroups的記憶體限制

JVM引數配置建議

  1. 使用容器感知的 JDK 版本。對於使用 Cgroup V1 的叢集,需要升級至 1.8.0_191以及更高版本;對於使用 Cgroup V2 的叢集,需要升級至 1.8.0_372、11.0.16及更高版本。
  2. 由於Java應用使用的總記憶體不僅僅只有堆記憶體,還有堆外記憶體和直接記憶體。所以設定容器記憶體上限時必須大於堆記憶體,應該按照 Java 程序使用的記憶體量上浮 20%~30% 設定容器記憶體 limit。如果初次執行程式,並不瞭解其實際記憶體使用量,可以先設定一個較大的 limit 讓程式執行一段時間,根據監控獲取實際平均使用值對容器記憶體 limit 進行調整。
  3. 如果在容器內僅執行一個Java 應用程式,則將初始堆大小與最大堆大小最好配置相等。如果不相等,JVM會根據堆記憶體使用量在Xms與Xmx之間動態修改堆記憶體大小,導致額外的系統開銷和頻繁的垃圾回收。
  4. 使用-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath引數,在JVM發生OOM時,自動生成dump檔案。dump檔案路徑最好是持久化掛載路徑避免容器重啟dump檔案丟失。

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章