条件
1. docker里运行java程序,docker容器限制了内存
1. jvm的heap内存必须常驻物理内存,不能被置换
1. jvm的native、direct内存不可控,也没办法限制上限
目标:如何保证运行java程序的docker容器不会因为内存超过限制而被os kill
我的想法:
1. jvm是否有参数可以让native、direct内存通过文件映射分配,通过映射文件去和容器剩余物理内存进行置换
2. docker容器打开swap开关,但如何保证jvm的heap常驻物理内存
跪求大佬带一把,感激不尽
不知道你这个问题是否已经解决, 如果还没有解决的话:要确定 docker 容器内存超限问题的直接原因并不难。直接进入docker容器,执行 top
命令,我们发现宿主机是一台8核16G的机器,而且 docker 并不会屏蔽这些信息,也就是 JVM 会认为自己工作于一台 16G 内存的机器上。而查看 demo 服务的 Dockerfile,发现运行服务时并没有对 JVM 的内存进行任何限制,于是 JVM 会根据默认的设置来工作 —— 最大堆内存为物理内存的1/4(这里的描述并不完全准确,因为 JVM 的默认堆内存大小限制比例其实是根据物理内存有所变化的,具体内容请自行搜索资料),而基于模板创建的 ServiceStage 流水线,在部署应用堆栈的时候会把 docker 容器的内存配额默认设置为 512M,于是容器就会在启动的时候内存超限了。至于以前没有碰到过这种问题的原因,只是因为以前没将这么高规格的 ECS 服务器用于流水线部署应用堆栈。
在查询过相关资料后,我们找到了两种问题解决方案,一个是直接在 jar 包运行命令里加上 -Xmx
参数来指定最大堆内存,不过这种方式只能将 JVM 堆内存限制为一个固定的值;另一个方法是在执行 jar 包时加上 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
参数,让 JVM 能够感知到docker容器所设置的 cgroup
限制,相应地调整自身的堆内存大小,不过这个特性是 JDK 8u131 以上的版本才具备的。
最终,我们提醒 ServiceStage 流水线的同学将 CSEJavaSDK demo 的创建模板做了改进,在 Dockerfile 中将打包的基础镜像版本由原先的 java:8u111-jre-alpine
升级为了 openjdk:8u181-jdk-alpine
,并且在运行服务 jar 包的命令中加上了 -Xmx256m
参数。问题至此已经解决了。