本文分享自華為雲社群《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
被棄用,使用MaxRAMPercentage
和MinRAMPercentage
來修改堆記憶體在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)
總結
Xms
和Xmx
能適應所有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版本開始棄用。MaxRAMPercentage
和MinRAMPercentage
在1.8.0_191版本開始啟用,可以自定義修改堆記憶體佔容器Cgroups記憶體限制的百分比。
JVM引數配置建議
- 使用容器感知的 JDK 版本。對於使用 Cgroup V1 的叢集,需要升級至 1.8.0_191以及更高版本;對於使用 Cgroup V2 的叢集,需要升級至 1.8.0_372、11.0.16及更高版本。
- 由於Java應用使用的總記憶體不僅僅只有堆記憶體,還有堆外記憶體和直接記憶體。所以設定容器記憶體上限時必須大於堆記憶體,應該按照 Java 程序使用的記憶體量上浮 20%~30% 設定容器記憶體 limit。如果初次執行程式,並不瞭解其實際記憶體使用量,可以先設定一個較大的 limit 讓程式執行一段時間,根據監控獲取實際平均使用值對容器記憶體 limit 進行調整。
- 如果在容器內僅執行一個Java 應用程式,則將初始堆大小與最大堆大小最好配置相等。如果不相等,JVM會根據堆記憶體使用量在Xms與Xmx之間動態修改堆記憶體大小,導致額外的系統開銷和頻繁的垃圾回收。
- 使用
-XX:+HeapDumpOnOutOfMemoryError
和-XX:HeapDumpPath
引數,在JVM發生OOM時,自動生成dump檔案。dump檔案路徑最好是持久化掛載路徑避免容器重啟dump檔案丟失。
點選關注,第一時間瞭解華為雲新鮮技術~