为啥我的 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。
提前致谢
【问题讨论】:
显示yourkit
和docker 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 容器与主机之间消耗两倍的内存的主要内容,如果未能解决你的问题,请参考以下文章
为啥我不能在我的 docker 容器中运行 phpinfo()?