容器环境Java的改进
Docker 和 K8S 的兴起,很多服务已经运行在容器环境。对于 Java 程序,JVM 设置是一个重要的环节.
非容器环境
非容器环境下,如果不指定最大堆内存,默认每个 JVM 最多使用 25% 的机器内存
如果指定JVM的最大堆内存,可以通过-Xmx指定最大堆内存
java -XX:+PrintFlagsFinal -Xmx1024m -version | grep -Ei "maxheapsize|maxram"uintx DefaultMaxRAMFraction = 4 {product}uintx MaxHeapSize := 1073741824 {product}uint64_t MaxRAM = 137438953472 {pd product}uintx MaxRAMFraction = 4 {product}double MaxRAMPercentage = 25.000000 {product}
java version "1.8.0_281"
Java(TM) SE Runtime Environment (build 1.8.0_281-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.281-b09, mixed mode)
但是需要注意的是,JVM 实际使用的内存会比 heap 内存大:
JVM内存 = heap 内存 + 线程stack内存 (XSS) * 线程数 + 启动开销(constant overhead)
默认的 XSS 通常在 256KB 到 1MB,也就是说每个线程会分配最少 256K 额外的内存, constant overhead 是 JVM 分配的其他内存。
容器环境的 Java Heap
但在容器环境下,Java 获取不到容器的内存限制(request、limit),只能获取到node节点的配置,因此无法使用JVM的默认配置。
如果使用-Xmx的配置方式,例如配置了容器限制使用1G内存,还需要再配置JVM的启动参数,徒增运维的复杂度。
为了解决这个问题,Java 10 引入了 +UseContainerSupport(默认情况下启用),通过这个特性,可以使得 JVM 在容器环境分配合理的堆内存。并且,在 JDK8U191 版本之后,这个功能引入到了 JDK 8。
UseContainerSupport
https://www.oracle.com/java/technologies/javase/8u191-relnotes.html
JDK 10 中引入了以下更改,以改进在 Docker 容器中运行的 Java 的执行和可配置性:
- JDK-8146115改进 docker 容器检测和资源配置使用
JVM 已被修改为知道它在 Docker 容器中运行,并将提取容器特定的配置信息而不是查询操作系统。提取的信息是已分配给容器的 CPU 数量和总内存。Java 进程可用的 CPU 总数是根据任何指定的 cpu 集、cpu 份额或 cpu 配额计算的。此支持仅适用于基于 Linux 的平台。默认情况下启用此新支持,可以在命令行中使用 JVM 选项将其禁用:
-XX:-UseContainerSupport
此外,此更改添加了一个 JVM 选项,该选项提供了指定 JVM 将使用的 CPU 数量的能力:
-XX:ActiveProcessorCount=count
此计数会覆盖 JVM 中的任何其他自动 CPU 检测逻辑。
- JDK-8186248允许更灵活地选择可用 RAM 的堆百分比
添加了三个新的 JVM 选项,以允许 Docker 容器用户对将用于 Java 堆的系统内存量进行更细粒度的控制:
-XX:InitialRAMPercentage
-XX:MaxRAMPercentage
-XX:MinRAMPercentage
这些选项取代了已弃用的分数形式(-XX:InitialRAMFraction
、-XX:MaxRAMFraction
和-XX:MinRAMFraction
)。
- JDK-8179498 attach in linux 应该相对于 /proc/pid/root 和命名空间感知
此错误修复更正了尝试从主机进程附加到在 Docker 容器中运行的 Java 进程时的附加机制。
请参阅JDK-8146115
最佳实践
设置 -XX:+UseContainerSupport,设置 -XX:MaxRAMPercentage=75.0,这样为其他进程(debug、监控)留下足够的内存空间,又不会太浪费 RAM。
注意:如果设置了-Xms,-XX:InitialRAMPercentage选项将被忽略。如果设置了-Xmx,-XX:MaxRAMPercentage选项将被忽略