为什么我的Java进程在Docker容器内消耗的内存是主机的两倍

8

我面临了一个有趣的问题,尝试分析在Docker容器和主机上运行的Java应用程序的内存消耗。

  1. Java应用程序是在Jetty服务器9.4.9上的Web应用程序
  2. Java版本:1.8
  3. 主机:MAC
  4. Docker镜像:jetty:9.4-jre8
  5. Docker守护程序版本为18.03.1-ce。

在主机上,我使用Yourkit工具来分析内存消耗。

对于docker容器docker stats <docker id/name>

我得到的是,在MAC上,yourkit显示了50M的非堆大小+大约40M的堆大小,总共约100M

enter image description here

而当我部署并在容器上运行相同的war文件时,统计数据显示我有200M

CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
879fb113ca8d        jetty-app           0.19%               214.6MiB / 1.952GiB   10.74%              1.49MB / 88.9kB     31.7MB / 6.42MB     29

有人能解释一下这个现象吗?

假设stats提供了错误的结果,我尝试使用--memory标志限制容器的内存,但效果不大,我仍然遇到了OOM。

提前感谢。


展示 yourkitdocker stats 的精确输出可能会很有用。 - Jaroslaw Pawlak
输出已添加 - Denis Voloshin
这里有几件事情。1. 注意MB和MiB之间的区别。2. docker stats可能包括线程堆栈使用的内存 - 默认情况下每个线程使用1MB,不包括在堆内存或非堆内存中,因此建议在yourkit中检查线程计数。3. yourkit显示的堆内存看起来有点奇怪 - 尽管应用程序仅使用了25MB的堆内存,但它可能保留了更多内存(绿线看起来像75MB)。 - Jaroslaw Pawlak
JVM本身可以使用100 MB的内存,特别是如果您有用于线程堆栈、套接字、缓冲区、JAR、JVM共享库等的本地内存。 - Peter Lawrey
谢谢大家的建议,我会尝试在Docker容器上运行YourKit,而不是使用docker stats命令。 - Denis Voloshin
1个回答

3
你可能需要再次尝试测量,使用OpenJDK 8u212或更高版本(2019年4月16日)。 (不要使用Oracle JDK,因为他们的许可证已更改)。
请参阅Grzegorz Kocur的“Java 8中的Docker支持-终于!”。

There is no need to use any hacky workarounds in a docker entrypoint, nor setting Xmx as fixed value anymore.

Docker support was also backported to Java 8.
Let’s check the newest openjdk image tagged as 8u212. We’ll limit the memory to 1G and use 1 CPU:

docker run -ti --cpus 1 -m 1G openjdk:8u212-jdk
你可以使用新的标志(已经存在于Java 10+中,但现在被移植到Java 8中)来微调堆大小,并且在这里解释
-XX:InitialRAMPercentage
-XX:MaxRAMPercentage
-XX:MinRAMPercentage

如果不希望使用新的JVM行为,可以使用-XX:-UseContainerSupport关闭它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接