如何防止Java超出容器内存限制?

6
我正在Docker容器中运行一个Java程序,该容器具有4GB的硬内存限制。我已将最大堆设置为3GB,但仍然超出限制,并被杀死(OOMKilled)。 我的问题是: 如何配置Java以遵守设置的容器限制,并抛出OutOfMemoryException而不是尝试分配超出限制并被主机内核处理? 更新: 我是一名经验丰富的Java开发人员,并且对JVM有相当的了解。我知道如何设置最大堆,但我想知道是否有一种方法可以将JVM进程从操作系统中要求的内存限制。

你能分享一下你用来运行JVM的完整命令吗? - BackSlash
1
@GeertSchuring 我们需要看看您是如何做到的。请分享完整的命令。 - BackSlash
这会触发一个“错误”,在出现“OOME”后,你很难恢复。为了防止JVM使用过多的内存,你需要减少应用程序中的内存消耗,或者升级分配更多内存的容器。 - AxelH
不要解释你认为自己做了什么。写下你正在使用的精确命令行(可能还要加上你正在使用的精确JRE版本)。 - GhostCat
3
请编辑您的问题,包括 java 控制台命令。对于未来的读者来说,评论中的更新可能不够明显。谢谢! - halfer
显示剩余3条评论
2个回答

6
当Java应用程序在容器内执行时,JVM自适应(负责根据主机的能力动态分配资源)不知道它正在容器内运行,并且它计算要由Java应用程序使用的资源数量是基于执行容器的主机。鉴于此,即使您设置了容器限制,JVM也将以主机的资源为基础进行计算。
从JDK 8u131+和JDK 9开始,有一种实验性的VM选项,允许JVM自适应从CGgroups中读取内存值。要启用它,您必须向JVM传递以下标志:
-XX:+UnlockExperimentalVMOptions和-XX:+UseCGroupMemoryLimitForHeap 如果您启用这些标志,JVM将意识到它正在容器内运行,并使JVM自适应根据容器限制而不是主机的能力来计算应用程序的资源。
启用标志:
$ java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -jar app.jar

您可以使用环境变量来动态传递JVM选项给容器。

例如:

运行应用程序的命令将会像这样:

 $ java ${JAVA_OPTIONS} -jar app.jar

docker run 命令需要像这样传递 ENV 变量:

$ docker run -e JAVA_OPTIONS="-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap" myJavaImage

希望这能有所帮助!

即使您对容器设置了限制,JVM 也会将主机的资源作为计算基础,这一点并不重要。明白了。(1)但这不是无视了名称空间和cgroups的全部意义吗?(2)从维基百科上可以看出,cgroup namespace类型自2016年3月以来就已经存在于Linux 4.6中。那么,这种新的cgroup namespace有什么意义呢?为什么应用程序(如JVM)需要关心添加容器支持?Docker引擎,例如containerd应默认具备此功能。这确实有些难以理解。 - user14305942

5
除了Fabian Rivera的回答外,我发现Java 10对于在容器中运行没有任何自定义启动参数有很好的支持。默认情况下,它使用容器内存的25%作为堆,这可能对一些用户来说有点低。您可以使用以下参数更改此设置:
-XX:MaxRAMPercentage=50

要尝试 Java 10,请运行以下 Docker 命令:

docker run -it --rm -m1g --entrypoint bash openjdk:10-jdk

它将为您提供一个bash环境,您可以在其中运行JDK中的可执行文件。例如,要运行一个小脚本,您可以像这样使用jrunscript

jrunscript -e "print(Packages.java.lang.Runtime.getRuntime().maxMemory()/(1<<20) + 'M')"

这将显示堆大小(以MB为单位)。要更改用于堆的总容器内存百分比,请添加MaxRAMPercentage参数,如下所示:

jrunscript -J-XX:MaxRAMPercentage=50 -e "print(Packages.java.lang.Runtime.getRuntime().maxMemory()/(1<<20) + 'M')"

现在您可以尝试调整容器的大小和堆的最大百分比。

"-XX:+PrintFlagsFinal"似乎不起作用。它仍然显示主机配置。这是为什么呢? - RamPrakash

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