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

时间:2018-08-27 13:53:22

标签: java docker containers memory-limit cgroups

我正在Docker容器内运行Java程序,该容器的硬盘内存限制为4GB。我将最大堆设置为3GB,但是Java程序仍然超出了限制并被杀死(OOMKilled)。

我的问题是:如何配置Java以遵守设置的容器限制并抛出OutOfMemoryException而不是尝试分配超出限制并被主机内核踢走?

>

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

2 个答案:

答案 0 :(得分:4)

在容器内执行Java应用程序时,JVM人机工程学(负责根据主机的功能动态分配资源)不知道它在容器内运行,并计算出要使用的资源数量基于执行容器的主机的Java应用程序。鉴于此,您是否对容器设置限制都没有关系,JVM将以您主机的资源为基础进行计算。

在JDK 8u131 +和JDK 9中,有一个实验性的VM选项,该选项允许JVM人机工程学从CGgroup读取内存值。要启用它,必须将以下标志传递给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

希望这会有所帮助!

答案 1 :(得分: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')"

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