Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent Java from exceeding the container memory limits?

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).

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?

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.

like image 964
Geert Schuring Avatar asked Aug 27 '18 13:08

Geert Schuring


People also ask

How do you restrict the memory utilization of a container?

To limit the maximum amount of memory usage for a container, add the --memory option to the docker run command. Alternatively, you can use the shortcut -m . Within the command, specify how much memory you want to dedicate to that specific container.

What happens when a container reaches the memory limit?

This is the first catch of container memory limitation. The --memory parameter limits the container memory usage, and Docker will kill the container if the container tries to use more than the limited memory.

What happens when Docker container reaches memory limit?

It has to crash. If you have a live-restore or on docker-swarm or on kubernetes it will bring up another container swiftly.

Why Java consumes more memory?

Java Virtual Machine optimizes the code during runtime. Again, to know which parts to optimize it needs to keep track of the execution of certain code parts. So again, you are going to lose memory.


1 Answers

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.

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 and -XX:+UseCGroupMemoryLimitForHeap

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.

Enabling the flags:

$ java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -jar app.jar

You can dynamically pass the JVM options to your container with ENV variables.

Example:

The command to run your app would like something like:

 $ java ${JAVA_OPTIONS} -jar app.jar

And the docker run command needs to pass the ENV variable like this:

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

Hope this helps!

like image 74
Fabian Rivera Avatar answered Sep 21 '22 02:09

Fabian Rivera