How to prevent Java from exceeding the container memory limits? - java

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.

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!

In addition to Fabian Rivera's answer I've found that Java 10 has good support for running in containers without any custom startup parameters. By default it uses 25% of the containers memory as heap, which might be a bit low for some users. You can change this with the following parameter:
-XX:MaxRAMPercentage=50
To play around with Java 10 run the following docker command:
docker run -it --rm -m1g --entrypoint bash openjdk:10-jdk
It will give you a bash environment where you can run executables from the JDK. For instance, to run a small piece of script you can use jrunscript like this:
jrunscript -e "print(Packages.java.lang.Runtime.getRuntime().maxMemory()/(1<<20) + 'M')"
This will show you the size of the heap in MB. To change the percentage of total container memory that is used for the heap add the MaxRAMPercentage parameter like this:
jrunscript -J-XX:MaxRAMPercentage=50 -e "print(Packages.java.lang.Runtime.getRuntime().maxMemory()/(1<<20) + 'M')"
Now you can play around with the sizing of the container and the max percentage of heap.

Related

How can I get the guest total available memory from inside a Docker container?

We have a Linux Docker container run on Kubernetes. We would like to set appropriate Xms and Xmx such as 75% of total available memory in the container.
However, it seems that /proc/meminfo is not namespaced (gets host memory instead of guest one), and that the cgroup memory limits are off the chart, meminfo returns correct on macos but likely will return total host memory on Linux host.
memory.limit_in_bytes
9223372036854771712
Do you have any idea how to correctly get the total memory available inside a Docker container in Kubernetes?
is there any env var?
Do you have any experience how to appropriately set Xmx directly or indirectly in a Docker in Kubernetes, so that it uses say 75% of the total available memory that is allocated to that container say 1.5G?
By finding out new flags in Java 10+, i solved my need in a different way: by not passing Xms or Xmx to the JVM, but the percentages below:
https://merikan.com/2019/04/jvm-in-a-container/#java-10
https://blog.arkey.fr/2020/10/27/maxrampercentage-is-not-what-i-wished-for/
https://github.com/openjdk/jdk11u/blob/master/src/hotspot/share/runtime/arguments.cpp -> set_heap_size function
percentage_of_memory_to_use_for_heap=${HEAP_SIZE_PERCENTAGE:-75}
java -XshowSettings:vm -XX:+UseContainerSupport \
-XX:InitialRAMPercentage=$((percentage_of_memory_to_use_for_heap)) \
-XX:MinRAMPercentage=$((percentage_of_memory_to_use_for_heap)) \
-XX:MaxRAMPercentage=$((percentage_of_memory_to_use_for_heap)) \

Can't launch jstat : Could not reserve enough space for object heap

I have an old Jboss server, with 5go of RAM.
My java app is configured like that :
...-server -Xms3200m -Xmx3500m -XX:MaxPermSize=512M...
When I try to shutdown, it uses the same JAVA_OPTS as the start, so it fail.
So I modify the shutdown.sh script, and add :
...
JAVA_OPTS='-Xms128m -Xmx128m -XX:MaxPermSize=128m'
export JAVA_OPTS
...
Everything works fine.
But now, it's my JMX stats which don't work anymore, I assume they uses jstat to mesure the free heap.
When I do a :
./jstat -gccapacity PID
I have the message :
Could not reserve enough space for object heap
But I have 1go of RAM free on the server at this time !
What JAVA_OPTS jstat use work ?
Most Java implementations that I worked with use a default Max Heap Size of 25% of system memory and your system probably does not allow overcommit of memory so this might cause this issue. (Source: https://docs.oracle.com/javase/7/docs/technotes/guides/vm/gc-ergonomics.html )
I hope this helps solve the issue:
For jstat you can set -Xmx but you need to prepend -J in front of it like:
jstat -gccapacity $YOUR_JBOSS_PID -J-Xmx10m -J-Xms10m -J-XX:+PrintFlagsFinal
PrintFlagsFinal will show you the effective settings that the JVM will start with, look for: MaxHeapSize for the effective value set by -Xmx or the equivalent: -XX:MaxHeapSize=128m
Source: https://docs.oracle.com/javase/7/docs/technotes/tools/share/jstat.html
Let me know it that helps (and if not, please provide some details like: java -version and uname -a )
See for more info and/or options also: https://chriswhocodes.com/vm-options-explorer.html

Increasing the JVM heap memory using Xmx doesn't work

I'm developping a javafx application which has much ui interfaces, and while opening many windows, the jvm start consumming much memory (going up tp 350mb).
When it arrives to 360mb, the programs starts lagging and end up by being crashed (nothing works, screen blocks ...) and the console show a OutOfMemoryException with Java Heap Space error
I've 6gb of memory in my computer, and tried to start the .jar file using -Xmx param, but still the operating system doesn't allow the jvm to consume more memory.
Is there anything else i should specify so that the jvm may be able to get as much memory as it needs ?
You might want to ensure that you're using:
java -Xmx1024m -jar YourApplication.jar
and not:
java -jar YourApplication.jar -Xmx1024m
Anything after the .jar is considered as argument passed to your executable Jar.

Jmeter, how to increase heap size

I had read that you need to change the heap size in the jmeter.bat file (I'm using windows) to increase the memory to be able to test around 500 threads or more. This is the default:
if not defined HEAP (
rem See the unix startup file for the rationale of the following parameters,
rem including some tuning recommendations
set HEAP=-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m
)
I changed the set HEAP=-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m it to:
set HEAP=-Xms2g -Xmx8g -XX:MaxMetaspaceSize=512m
But when I open Jmeter in GUI mode there is a message in the command line window
Modify current env variable HEAP="-Xms1g -Xmx1g -XX:MaxMetaSpaceSize=256m" in the jmeter batch file
So does that mean the change in the batch file didn't work? Or it does work when running in non-gui mode? Did I miss something to change or what? Thanks in advance.
You don't need to increase the heap unless you face java.lang.OutOfMemoryError: Java heap space error or detect unusually high GC activity
The message basically a form of advice, you will see it even if you have several terabytes of heap space as it is simply hard-coded and will always be shown during GUI startup. If you want to see all Java arguments including heap space settings you can use JSR223 Sampler and the following Groovy code:
java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments().each {
log.info("Effective JVM argument: " + "$it")
}
This way you can test whether you changes are applied or not:

JMeter Dashboard Generation Java Heap Space

I've been able to create dashboards for small amounts of log data (3mb) with JMeter. However, when trying to create dashboards with large amounts of data (35mb), jmeter will throw a java.lang.OutOfMemoryError: Java Heap Space.
So far I've tried to create an environment variable called JVM_ARGS=-Xms1024m -Xmx10240m but I still do not have enough space.
Is there anything else I can try to create these dashboards? Or is there a way to reduce the number of entries that get written to the log file?
Thank you!
There are 2 possibilities :
Option 1 : your JVM options are not taken into account. Show the first lines or all content of jmeter.log
Option 2 : you have added some dynamic parameter to your http requests that has created a lot of different (name) SampleResult
Edit 8 october 2018:
Root cause was point Option 2
Make sure you've really created the environment variable and it has the anticipated value, double check this by running the following command in the terminal window where you will be launching JMeter from:
echo %JVM_ARGS% for Windows
echo $JVM_ARGS for Linux/Unix/MacOS
You should see your increased JVM heap settings
Make sure to use either jmeter.bat for Windows or jmeter.sh for other operating systems wrapper script
Make sure to use 64-bit version of JRE as 32-bit will not be able to allocate more than 3G heap
Make sure you can execute java command with your 10G heap
java -Xms1024m -Xmx10240m -version
you should see your Java version
Try running ApacheJMeter.jar executable directly:
java -Xms1024m -Xmx10240m -jar ApacheJMeter.jar -g result.jtl -o destination_folder
If nothing helps be aware that you can generate tables/charts using JMeterPluginsCMD Command Line Tool (it is not a part of standard JMeter installation, can be installed using JMeter Plugins Manager)

Categories