了解Docker中的Java内存行为

我们目前运行在Google容器引擎(GKE)中的Kubernetes集群由运行由Google维护的Debian GNU / Linux 7.9(wheezy)的n1-standard-1机器types(1个虚拟CPU和3.75 GB内存)组成。 由于我们服务的负载和内存使用量增加,我们需要将我们的节点升级到更大的机器types。 在我们的testing集群中尝试这个时,我们经历了一些对我们来说似乎很奇怪的事情。

JVM应用程序在部署到Google节点时消耗的内存似乎与节点上可用的内核数成正比。 即使我们将JVM最大内存(Xmx)设置为128Mb,在一台内核计算机上也会消耗大约250Mb(这是可以理解的,因为JVM由于GC,JVM本身等消耗的内存超过最大限制) 2核心机器上的700Mb( n1-standard-2 )和4核心机器上的1.4Gb( n1-standard-4 )。 唯一不同的是机器types,使用的是相同的Docker镜像和configuration。

例如,如果我使用n1-standard-4机器typesSSH进入一台机器,并运行sudo docker stats <container_name>我得到这个:

 CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O k8s.name 3.84% 1.11 GB / 1.611 GB 68.91% 0 B / 0 B 

当我使用完全相同的(应用程序)configuration在本地运行相同的 Docker镜像(mac osx和docker-machine)时,我看到:

 CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O name 1.49% 236.6 MB / 1.044 GB 22.66% 25.86 kB / 14.84 kB 0 B / 0 B 

这更符合我期望的Xmx设置(logging我有8个内核和16Gb的内存)。 当我在GKE实例上运行top -p <pid>时,同样的事情得到证实,这给了我一个1.1到1.4Gb的RES / RSS内存分配。

Docker镜像是这样定义的:

 FROM java:8u91-jre EXPOSE 8080 EXPOSE 8081 ENV JAVA_TOOL_OPTIONS -Dfile.encoding=UTF-8 # Add jar ADD uberjar.jar /data/uberjar.jar CMD java -jar /data/uberjar.jar -Xmx128m -server 

我也试过添加:

 ENV MALLOC_ARENA_MAX 4 

我已经在几个线程中看到了这样的build议,但是它似乎没有什么区别。 我也尝试改变到另一个Java基础的形象,以及使用高山Linux,但这似乎并没有改变的事情。

我的本地Docker版本是1.11.1,Kubernetes / GKE中的Docker版本是1.9.1。 Kubernetes版本(如果有的话)是v1.2.4。

另外有趣的是,如果我们将pod /容器的多个实例部署到同一台机器/节点,一些实例将分配更less的内存。 例如,前三个可能分配1.1-1.4Gb的内存,但是后来的10个容器只能分配大约250Mb,这大概是我期望每个实例分配的。 问题是,如果我们达到了机器的内存限制,前三个实例(分配1.1Gb内存的实例)似乎从未释放分配的内存。 如果他们要释放内存时,机器是在增加压力我不会担心这个,但由于他们保留内存分配,即使当机器加载成为一个问题(因为它禁止其他容器计划在这台机器上,从而浪费资源)。

问题:

  1. 什么可能导致这种行为? 这是一个JVM的问题? Docker的问题? VM问题? Linux的问题? configuration问题? 或者也许是一个组合?
  2. 在这种情况下,我可以尝试做些什么来限制JVM的内存分配?

当你指定

 CMD java -jar /data/uberjar.jar -Xmx128m -server 

那么您打算成为JVM参数( -Xmx128m -server )的值将作为命令行parameter passing给.jar文件中的Main类。 它们可用作public static void main(String... args)方法中的public static void main(String... args)

请注意,如果您按名称运行主类,而不是指定可执行的jar文件,则也是如此。

要将它们作为JVMparameter passing,而不是传递给程序,则需要在-jar arg之前指定它们。

请参阅https://stackoverflow.com/questions/5536476/passing-arguments-to-jar-which-is-required-by-java-interpreter

问题是在Dockerfile指定的JVM设置。 我们这样开始我们的JVM:

 CMD java -jar /data/uberjar.jar -Xmx128m -server 

但是当我们切换到:

 CMD java -Xmx128m -server -jar /data/uberjar.jar 

设置被考虑在内。 我仍然不明白为什么我没有看到这个地方,但我很高兴我们设法解决它。