Docker作为ServerLess的新的软件架构和场景,Java势必会融入到这个环境之中,那么我们就说一下Java在Docker中的运行

Docker中的内存,CPU等资源限制是用过CGroup进行实现的,JDK并不能识别这些限制

如果没有设置合适的JVM堆和元数据区,直接内存,可能JVM使用超过限制的内存,导致的OOM,如果判断错误了可以获取的CPU资源,Docker限制了CPU核数,JVM就可能设置不合适的GC并行线程

那么Docker这种运行环境,对于Java有什么问题?

先Pass Docker本身的实现,我们说下Docker本身没有隐藏的一些底层信息,

首先CGroup作为新兴的技术,一些老版本的JDK无法理解这些资源限制

而且,namespace对于容器的应用细节有了些微妙的差异,jcmd,jstack等工具,依赖于 “/proc/” 下的部分信息,Docker的设计改变了这部分信息的结构

比如说,Jvm会在启动时候检测内存大小,将初始堆的大小设置为内存上限的 1/64,将对堆的最大值设置为内存的1/4

并且检测系统的CPU核数,影响到了Parllel GC的并发线程数目和JIT complier线程数目,应用中的ForkJoinPool机制的并行等级

由于老版本的JVM可能对这个判断是基于错误信息进行的,所以导致服务的错误

最为简单的解决方案就是升级JDK的版本

JDK9中引入了一些实验性的参数,方便让JDK和Docker沟通

针对内存的限制,可以设置如下参数

-XX:+UnlockExperimentalVMOptions

-XX:+UseCGroupMemoryLimitForHeap

这两个参数顺序敏感,只支持Linux,这样方便Java知道cgroup的设置

如果是JDK10的环境,问题更加简单了,Java的容器支持已经比较完善,默认适应各种资源限制和实现差异

-XX:+UseCGroupMemoryLimitForHeap 已经被标记为废弃

新增了参数可以指定CPU 核心的数目

-XX:+UseCGroupMemoryLimitForHeap

而且,还提供了关闭容器支持特性,作为一种防御性机制,避免新特性破坏原有基础功能

对于老版本的JDK的设置

我们需要明确设置 堆 元数据区 等内存区域的大小

比如Docker,可以设置对应的启动参数

$ docker run -it –rm –name yourcontainer -p 8080:8080 -m 800M repo/your-java-container:openjdk

配置如下的环境变量,指定JVM堆的大小

-e JAVA_OPTIONS=’-Xmx300m’

明确的设置GC和JIT并行线程数目,

-XX:ParallelGCThreads

-XX:CICompilerCount

然后就是避免使用SWAP,告诉JVM实际能够使用的内存上限

-XX:MaxRAM=`cat /sys/fs/cgroup/memory/memory.limit_in_bytes`

设置Docker的运行参数

-memorty-swappiness=0

我们显式的关闭了SWAP,就可以避免直接的OOM了

对于使用的JDK 9之后的版本,可以利用 jlink 定制最小依赖的Java运行环境,将JDK裁减为几十M的大小

发表评论

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