前言
将 Java 应用容器化虽然更好地解决了可移植性问题,但也存在着一些不友好的情况,比如低版本的JDK(低于Java 8u131)并不能识别 CGroup 资源限制。这将导致JVM读取的是宿主机的全部CPU和内存,一但容器使用资源超过限制则会被 docker 杀死。
在 kubernetes 中,我们会显示在 yaml 文件中配置CPU、内存请求和限制,我们希望容器中的JVM进程能够自动识别到 CGroup 资源限制,获取到正确的内存和CPU信息从而自行动态调整。
JVM 参数配置
以下操作皆在一台 4C 16G 服务器上进行。
版本低于 8u131
JDK 版本低于 8u131 版本的 JVM 不会自动识别到 CGroup 资源限制,需要手动设置初始堆大小以及最大堆大小,否则会按照宿主机的全部内存设置默认值:
- 配置最大堆大小
-Xmx
,默认值:内存的1/4 - 配置初始堆大小
-Xms
,默认值:内存的1/64
未配置JVM参数
可以看到 Max. Heap Size (Estimated): 3.48G
,未能正确识别 CGroup 资源限制
$ docker run --rm -m 2GB openjdk:8u121-alpine java -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 3.48G
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM
openjdk version "1.8.0_121"
OpenJDK Runtime Environment (IcedTea 3.3.0) (Alpine 8.121.13-r0)
OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)
配置JVM参数
配置 -Xmx
和 -Xms
后即可达到我们想要的结果
$ docker run --rm -m 2GB openjdk:8u121-alpine java -XshowSettings:vm -Xmx2000m -Xms2000m -version
VM settings:
Min. Heap Size: 1.95G
Max. Heap Size: 1.95G
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM
openjdk version "1.8.0_121"
OpenJDK Runtime Environment (IcedTea 3.3.0) (Alpine 8.121.13-r0)
OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)
8u131 及以上版本
从 8u131 版本开始支持 UseCGroupMemoryLimitForHeap
和 MaxRAMFraction
这两个选项,用 CGroupMemory
的大小作为 JVM heap size,MAXRAMFraction
是用来控制实际可用的内存数量的,比如设置为 1 的话就是 CGroupMemoryLimit
的全部,设置为 2 的话一半,3 的话就是 1/3,以此类推
| MaxRAMFraction取值 | 堆占比 | 容器内存=1G | 容器内存=2G | 容器内存=4G | 容器内存=8G | 容器内存=16G | | :--------------------: | :--------- | :-------------: | :-------------- | :-------------: | :-------------- | :--------------: || | | | | | | | 1 | ≈90% | 910.50M | 1.78G | 3.56G | 7.11G | 14.22G | | 2 | ≈50% | 455.50M | 910.50M | 1.78G | 3.56G | 7.11G | | 3 | ≈33% | 304.00M | 608.00M | 1.19G | 2.37G | 4.74G | | 4 | ≈25% | 228.00M | 455.50M | 910.50M | 1.78G | 3.56G |
未配置JVM参数
可以看到 Max. Heap Size (Estimated): 3.48G
,未能正确识别 CGroup 资源限制
$ docker run --rm -m 2GB openjdk:8u131-alpine java -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 3.48G
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (IcedTea 3.4.0) (Alpine 8.131.11-r2)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)
配置JVM参数
配置 -XX:+UnlockExperimentalVMOptions
、-XX:+UseCGroupMemoryLimitForHeap
和 -XX:MaxRAMFraction=1
后即可达到我们想要的结果
$ docker run --rm -m 2GB openjdk:8u131-alpine java -XshowSettings:vm -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -version
VM settings:
Max. Heap Size (Estimated): 1.78G
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (IcedTea 3.4.0) (Alpine 8.131.11-r2)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)
8u191 及以上版本
从 8u191 开始引入了 java10+ 上的 UseContainerSupport
选项,而且是默认启用的,不用设置。同时 UseCGroupMemoryLimitForHeap
这个就弃用了,不建议继续使用,同时还可以通过 -XX:InitialRAMPercentage
、-XX:MaxRAMPercentage
、-XX:MinRAMPercentage
这些参数更加细腻的控制 JVM 使用的内存比率。比如一些 Java 程序在运行时会调用外部进程、申请 Native Memory 等,所以即使是在容器中运行 Java 程序,也得预留一些内存给系统的。所以 -XX:MaxRAMPercentage
不能配置得太大。
未配置JVM参数
可以看到未添加任何 JVM 参数即可正确识别到 CGroup 资源限制
$ docker run --rm -m 2GB openjdk:8u191-alpine java -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 455.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)
配置JVM参数
- 使用
-XX:MaxRAMFraction
参数调整Max. Heap Size
大小
$ docker run --rm -m 2GB openjdk:8u191-alpine java -XX:MaxRAMFraction=1 -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 1.78G
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)
- 使用
-XX:InitialRAMPercentage
、-XX:MaxRAMPercentage
、-XX:MinRAMPercentage
参数更加细腻的控制 JVM 使用的内存比率
$ docker run --rm -m 2GB openjdk:8u191-alpine java -XX:InitialRAMPercentage=40.0 -XX:MaxRAMPercentage=90.0 -XX:MinRAMPercentage=50.0 -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 1.60G
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)