为啥我的 Java 进程在 docker 容器与主机之间消耗两倍的内存

Posted

技术标签:

【中文标题】为啥我的 Java 进程在 docker 容器与主机之间消耗两倍的内存【英文标题】:Why does my Java process consumes twice memory inside a docker container vs host为什么我的 Java 进程在 docker 容器与主机之间消耗两倍的内存 【发布时间】:2018-11-16 02:05:29 【问题描述】:

我在尝试分析在 docker 容器与主机上运行的 Java 应用程序中的内存消耗时遇到了一个有趣的问题。

    Java 应用是 Jetty 服务器 9.4.9 上的 Web 应用 Java 版本:1.8 主机:MAC Docker 镜像:jetty:9.4-jre8 docker daemon 是 18.03.1-ce 版本。

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

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

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

然而,当我在容器上部署和运行相同的战争时,统计数据显示我 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 的确切输出可能很有用。 添加的输出 这里有几件事。 1. 注意 MB 和 MiB 之间的区别。 2. docker stats 可能包括线程堆栈使用的内存 - 默认情况下,每个线程 1MB,不包括在堆内存或非堆内存中,所以我建议检查你的工具包中的线程数。 3. yourkit 显示的堆内存看起来有点奇怪 - 虽然应用程序只使用了 25MB 的堆内存,但它可能比保留的更多(绿线看起来像 75MB)。 JVM 本身可以使用 100 MB,特别是如果您有用于线程堆栈、套接字、缓冲区、JAR、JVM 共享库等的本机内存。 感谢大家的提示,我将尝试在 docker 容器上运行 yourkit,而不是使用 docker stats 命令 【参考方案1】:

您可能想再次尝试测量,使用openJDK 8u212 或更多(2019 年 4 月 16 日)。 (没有Oracle JDK,因为their license has changed)

参见Grzegorz Kocur 中的“Docker support in Java 8 — finally!”。 现在:

无需在 docker 入口点中使用任何 hacky 变通方法,也无需再将 Xmx 设置为固定值。

Docker 支持也向后移植到 Java 8。 让我们检查最新的标记为 8u212 的 openjdk 映像。我们将内存限制为 1G 并使用 1 个 CPU:

docker run -ti --cpus 1 -m 1G openjdk:8u212-jdk

您可以使用新标志(Java 10+ 中已经存在,但现在重新移植到 Java 8)和 explained here 来微调堆大小。

-XX:InitialRAMPercentage
-XX:MaxRAMPercentage
-XX:MinRAMPercentage

如果由于某种原因不需要新的 JVM 行为,可以使用 -XX:-UseContainerSupport 将其关闭。

【讨论】:

以上是关于为啥我的 Java 进程在 docker 容器与主机之间消耗两倍的内存的主要内容,如果未能解决你的问题,请参考以下文章

为啥我ping不通我的docker容器

为啥我不能在我的 docker 容器中运行 phpinfo()?

为啥 Docker 容器在启动时会被静默删除

为啥我的 udev 规则在运行的 docker 容器中不起作用?

为啥我的容器没有使用我主机的可用 RAM?

为啥我的文件加载线程没有与主线程并行?