版权归作者所有,任何形式转载请联系作者。
作者:Java大蜗牛(来自豆瓣)
来源:https://www.douban.com/note/672258250/

很多运行在Java虚拟机(JVM)中的应用,包括数据服务如Apache Spark和Kafka以及传统企业应用,都运行在容器中。最近,运行在容器里的JVM出现了由于内存和CPU资源限制和使用率导致性能损失问题。这是因为Java意识不到自己运行在容器中。随着Java 10的发布,JVM终于可以通过设置cgroup来实现这些约束。内存和CPU约束都可以直接在容器里被用来管理Java应用,包括:

在容器里面设置内存约束
在容器里设置可用的CPUs个数
在容器里设置CPU约束
Java 10的这些优化在Docker for mac/windows和Docker企业版均可正常运行。

容器内存限制
一直到Java 9,JVM依然不能够通过在容器中使用标识来识别内存或CPU限制。在Java 10中,内存限制可以被自动识别,而且这个特性是默认支持的。

Java对服务器进行分级定义,如果一台服务器有2CPUs和2GB内存,那么默认的堆栈大小是1/4的物理内存。假设一个安装了Docker企业版的服务器有4CPUs和2GB内存。我们来比较分别比较运行Java 8和Java 10的容器的区别。先看Java 8:

shell
dockercontainerrun-it-m512–entrypointbashopenjdk:latest

$docker-java-home/bin/java-XX:PrintFlagsFinal-version|grepMaxHeapSize
uintxMaxHeapSize:=524288000{product}
openjdkversion”1.8.0_162″

我们可以看到最大的堆栈大小是512MB,刚好(1/4)X2GB,只是通过Docker EE自动设置的,而不是通过设置容器来实现的。做个比较,我们看下在Java 10环境下运行同样的命令是什么结果。

shell
dockercontainerrun-it-m512M–entrypointbashopenjdk:10-jdk

$docker-java-home/bin/java-XX:PrintFlagsFinal-version|grepMaxHeapSize
size_tMaxHeapSize=134217728{product}{ergonomic}
openjdkversion”10″2018-03-20

上面的结果显示,容器里的内存限制非常接近我们期望的128MB。

设置可用CPUs个数
默认情况下,每个容器可以获取到主机的CPU时钟周期是没有限制的。各种限制可以被设置来约束某个指定的容器可以获取的主机CPU时钟周期。

Java 10能够识别这些限制:

shell
dockercontainerrun-it–cpus2openjdk:10-jdk
jshell>Runtime.getRuntime().availableProcessors()
$1==>2

所有分配给Docker EE的CPUs均获得同样比例的CPU时钟周期。这个比例可以通过调整CPU共享比重(相对于其他所有运行的容器的比重)来进行修改。

这个比例只在运行CPU敏感的进程才生效。当一个容器中的任务是空闲状态,那么其他容器可以使用剩余的全部CPU时间。然后真实的CPU时间统计则相当依赖系统上运行的容器的个数。这些在Java 10中都可以设置:

shell
dockercontainerrun-it–cpu-shares2048openjdk:10-jdk
jshell>Runtime.getRuntime().availableProcessors()
$1==>2

Java 10中也可以设置CPU集合约束(允许哪些CPU执行)。

shell
dockerrun-it–cpuset-cpus=”1,2,3″openjdk:10-jdk
jshell>Runtime.getRuntime().availableProcessors()
$1==>3

分配内存和CPU
使用Java 10,可以使用容器设置来估计部署应用程序所需的内存和CPU的分配。我们假设已经确定了容器中运行的每个进程的内存堆和CPU要求,并设置了JAVA_OPTS。例如,如果您有一个跨10个节点分布的应用程序,五个节点需要512Mb的内存,每个需要1024个CPU-shares,另外五个节点需要256Mb,每个512个CPU-shares。请注意,1个CPU份额比例由1024表示。

对于内存,应用程序需要至少分配5Gb。
512Mb x 5 = 2.56 Gb
256Mb x 5 = 1.28 Gb

该应用程序需要8个CPU才能高效运行。
1024 x 5 = 5 CPUs
512 x 5 = 3 CPUs

最佳实践建议分析应用程序以确定运行在JVM中的每个进程的内存和CPU分配。但是,在调整容器大小以防止Java应用程序中的内存不足错误以及分配足够的CPU来处理工作负载时,Java 10消除了这些猜测。

发表评论

电子邮件地址不会被公开。 必填项已用*标注