本文分享自华为云社区《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
,参数值必须为整数,取值参考如下表格:
[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 内存限制的百分比。
以上仅适用于容器采用 Cgroups v1 版本,在 Cgroups v2 版本中 jdk 需要 1.8.0_372、11.0.16 及更高版本才能动态感知 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 文件丢失。
点击关注,第一时间了解华为云新鲜技术~
评论