问题描述
我在一个硬内存限制为 4GB 的 Docker 容器中运行 Java 程序.我已将最大堆设置为 3GB,但 Java 程序仍然超出限制并被终止(OOMKilled).
I'm running a Java program inside a Docker container that has a hard memory limit of 4GB. I've set the max heap to 3GB but still the Java program exceeds the limit and gets killed (OOMKilled).
我的问题是:如何配置 Java 以遵守设置的容器限制并抛出 OutOfMemoryException 而不是尝试分配超出限制并让主机内核踢它的屁股?
My question is: How can I configure Java to respect the set container limit and throw an OutOfMemoryException instead of trying to allocate beyond the limit and get its ass kicked by the host kernel?
更新:我是一名经验丰富的 Java 开发人员,对 JVM 有相当的了解.我知道如何设置最大堆,但我想知道是否有人知道如何为 JVM 进程从操作系统声明的 total 内存设置限制.
Update: I'm an experienced Java developer and have a fair understanding of the JVM. I know how to set the max heap, but I wonder if anyone knows of a way to set a limit to the total memory that the JVM process claims from the OS.
推荐答案
当Java应用程序在容器内执行时,JVM人体工程学(负责根据主机能力动态分配资源)并不知道它正在运行在容器内,它会根据正在执行容器的主机计算 Java 应用程序要使用的资源数量.鉴于此,无论您是否为容器设置限制,JVM 都会以您主机的资源为基础进行计算.
When a Java application is executed inside a container, the JVM ergonomics (which is responsible for dynamically assign resources based on the host's capabilities) does not know it is running inside a container and it calculates the number of resources to be used by the Java app based on the host that is executing your container. Given that, it does not matter if you set limits to your container, the JVM will take your host's resources as the base for doing that calculation.
从 JDK 8u131+ 和 JDK 9 开始,有一个实验性的 VM 选项允许 JVM 人体工程学从 CGgroups 读取内存值.要启用它,您必须将以下标志传递给 JVM:
From JDK 8u131+ and JDK 9, there’s an experimental VM option that allows the JVM ergonomics to read the memory values from CGgroups. To enable it you must pass the following flags to the JVM:
-XX:+UnlockExperimentalVMOptions 和 -XX:+UseCGroupMemoryLimitForHeap
如果您启用这些标志,JVM 将知道它正在容器内运行,并使 JVM 符合人体工程学,根据容器限制而不是主机的能力来计算应用程序的资源.
If you enable these flags, the JVM will be aware that is running inside a container and will make the JVM ergonomics to calculate the app's resources based on the container limits and not the host's capabilities.
启用标志:
$ java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -jar app.jar
您可以使用 ENV 变量将 JVM 选项动态传递给您的容器.
You can dynamically pass the JVM options to your container with ENV variables.
例子:
运行您的应用程序的命令类似于:
The command to run your app would like something like:
$ java ${JAVA_OPTIONS} -jar app.jar
而 docker run 命令需要像这样传递 ENV 变量:
And the docker run command needs to pass the ENV variable like this:
$ docker run -e JAVA_OPTIONS="-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap" myJavaImage
希望这会有所帮助!
这篇关于如何防止 Java 超出容器内存限制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!