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

问题描述 投票:3回答:2

我正在Docker容器中运行一个Java程序,其内存限制为4GB。我已将最大堆设置为3GB,但Java程序仍然超出限制并被杀死(OOMKilled)。

我的问题是:我如何配置Java以尊重设置容器限制并抛出OutOfMemoryException而不是尝试分配超出限制并让主机内核踢它的屁股?

更新:我是一位经验丰富的Java开发人员,对JVM有一个公平的理解。我知道如何设置最大堆,但我想知道是否有人知道如何设置JVM进程从操作系统声明的总内存限制。

java docker containers memory-limit cgroups
2个回答
4
投票

当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

您可以使用ENV变量将JVM选项动态传递到容器。

例:

运行您的应用程序的命令类似于:

 $ java ${JAVA_OPTIONS} -jar app.jar

docker run命令需要传递ENV变量,如下所示:

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

希望这可以帮助!


2
投票

除了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')"

现在,您可以使用容器的大小和堆的最大百分比。

© www.soinside.com 2019 - 2024. All rights reserved.