在 docker 容器中运行的 JVM(更新 131 之前的 JDK 8)忽略了容器环境设置的 CGroup 限制。而且,他们查询的是主机资源,而不是分配给容器的资源。结果对 JVM 来说是灾难性的,即当 JVM 试图为自己分配比 CGroup 限制所允许的更多的资源(CPU 或内存)时,如果 java 程序是以 pid 1 运行。
内存问题的解决方案 -(可能在 JDK 8 更新 131 中修复)如上所述,JVM 分配给它自己的内存比容器允许的内存多。这可以通过以下方式轻松解决
-Xmx
在启动 JVM 时显式设置最大堆内存限制(使用)。(131更新前)-XX:+UnlockExperimentalVMOptions
和-XX:+UseCGroupMemoryLimitForHeap
解决 CPU 问题(可能已在 JDK 更新 212 中修复) 再次如上所述,在 docker 中运行的 JVM 将直接查看主机硬件并获取可用的 CPU 总数。然后它会尝试根据此 CPU 计数进行访问或优化。
docker run -ti --cpus 1 -m 1G openjdk:8u212-jdk
//在这个容器中运行的jvm被限制为1cpu。Linux 容器支持首先出现在 JDK 10 中,然后移植到 8u191,参见JDK-8146115。
早期版本的 JVM 获取可用 CPU 的数量如下。
在 8u121 之前,HotSpot JVM 依赖于sysconf(_SC_NPROCESSORS_ONLN)
libc 调用。反过来,glibc 读取系统文件/sys/devices/system/cpu/online
。因此,为了伪造可用 CPU 的数量,可以使用绑定安装替换此文件:
echo 0-3 > /tmp/online
docker run --cpus 4 -v /tmp/online:/sys/devices/system/cpu/online ...
要仅设置一个 CPU,请写入echo 0
而不是echo 0-3
从 8u121 开始,JVM 就可以识别任务集了。取而代之的是sysconf
,它开始调用sched_getaffinity
以查找进程的 CPU 关联掩码。
这打破了绑定安装技巧。不幸的是,你不能sched_getaffinity
像sysconf
. 但是,可以sched_getaffinity
使用LD_PRELOAD替换 libc 实现。
我写了一个小的共享库proccount来代替sysconf
和sched_getaffinity
。因此,该库可用于在 8u191 之前的所有 JDK 版本中设置正确的可用 CPU 数量。
这个怎么运作
首先,它读取cpu.cfs_quota_us
并cpu.cfs_period_us
查找容器是否使用--cpus
选项启动。如果两者都大于零,则 CPU 的数量估计为
cpu.cfs_quota_us / cpu.cfs_period_us
否则,它会读取cpu.shares
并估计可用 CPU 的数量为
cpu.shares / 1024
这种 CPU 计算类似于它在现代容器感知 JDK 中的实际工作方式。
该库定义(覆盖)sysconf
和sched_getaffinity
函数以返回在(1)或(2)中获得的处理器数量。
如何编译
gcc -O2 -fPIC -shared -olibproccount.so proccount.c -ldl
如何使用
LD_PRELOAD=/path/to/libproccount.so java <args>
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句