本文分享自華爲雲社區《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文件丟失。