54

Java 8/9 brought support for -XX:+UseCGroupMemoryLimitForHeap (with -XX:+UnlockExperimentalVMOptions). This sets -XX:MaxRAM to the cgroup memory limit. Per default, the JVM allocates roughly 25% of the max RAM, because -XX:MaxRAMFraction defaults to 4.

Example:

MaxRAM = 1g
MaxRAMFraction = 4
JVM is allowed to allocate: MaxRAM / MaxRAMFraction = 1g / 4 = 256m

Using only 25% of the quota seems like waste for a deployment which (usually) consists of a single JVM process. So now people set -XX:MaxRAMFraction=1, so the JVM is theoretically allowed to use 100% of the MaxRAM.

For the 1g example, this often results in heap sizes around 900m. This seems a bit high - there is not a lot of free room for the JVM or other stuff like remote shells or out-of-process tasks.

So is this configuration (-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1) considered safe for prod or even best practice? Or should I still hand pick -Xmx, -Xms, -Xss and so on?

atamanroman
  • 11,607
  • 7
  • 57
  • 81
  • Related: https://stackoverflow.com/questions/46211535/how-do-i-elegantly-and-safely-maximize-the-amount-of-heap-space-allocated-to-a-j – atamanroman Apr 16 '18 at 09:46
  • Did you read [Running a JVM in a container without getting killed](https://blog.csanchez.org/2017/05/31/running-a-jvm-in-a-container-without-getting-killed/)? – Mansuro Apr 16 '18 at 09:54
  • 2
    I did. Someone asked in the comments whether allocating more than 90% of the available RAM for heap was safe and the author replied "Supposedly the MaxRAMFraction=1 will still leave some room for the other non-heap memory. I haven’t done extensive testing though". Does not seem like a definitive answer. – atamanroman Apr 16 '18 at 10:27
  • 7
    I know you are asking for Java8/9, but just an fyi Java 10 has added new option -XX:MaxRAMPercentage which is more sensible and gives more flexibility to specify the max heap size. So you can say -XX:MaxRAMPercentage=80 to tell JVM to use 80% of container memory as the heap. And the default max heap size is also closer to the container memory limit. – Ashutosh May 16 '18 at 07:01
  • why is -Xss used for – harsha kumar Reddy Apr 05 '23 at 09:32

2 Answers2

49

We did some simple testing which showed that setting -XX:MaxRAM=$QUOTA and -XX:MaxRAMFraction=1 results in killed containers under load. The JVM allocates more than 900M heap, which is way too much. -XX:MaxRAMFraction=2 seems safe(ish).

Keep in mind that you may want to leave headroom for other processes like getting a debug shell (docker exec) or diagnostics in the container.


Edit: we've written up what we've learned in detail in an article. Money quotes:

TL'DR: Java memory management and configuration is still complex. Although the JVM can read cgroup memory limits and adapt memory usage accordingly since Java 9/8u131, it’s not a golden bullet. You need to know what -XX:+UseCGroupMemoryLimitForHeap does and you need to fine tune some parameters for every deployment. Otherwise you risk wasting resources and money or getting your containers killed at the worst time possible. -XX:MaxRAMFraction=1 is especially dangerous. Java 10+ brings a lot of improvements but still needs manual configuration. To be safe, load test your stuff.

and

The most elegant solution is to upgrade to Java 10+. Java 10 deprecates -XX:+UseCGroupMemoryLimitForHeap (11) and introduces -XX:+UseContainerSupport (12), which supersedes it. It also introduces -XX:MaxRAMPercentage (13) which takes a value between 0 and 100. This allows fine grained control of the amount of RAM the JVM is allowed to allocate. Since +UseContainerSupport is enabled by default, everything should work out of the box.


Edit #2: we've written a little bit more about -XX:+UseContainerSupport

Java 10 introduced +UseContainerSupport (enabled by default) which makes the JVM use sane defaults in a container environment. This feature is backported to Java 8 since 8u191, potentially allowing a huge percentage of Java deployments in the wild to properly configure their memory.

atamanroman
  • 11,607
  • 7
  • 57
  • 81
  • 2
    We have done similar testing. We found that the non-heap memory allocation tends to be fairly fixed. We tested with Spring Boot 2, we had to set the fraction to 4, if we had the memory around 500MB, but as we went out to 1G, we could drop down. And for even larger containers, 90% left enough for the app to perform fine. (4gb memory in the container.) – GreenKiwi May 23 '18 at 15:53
  • 4GB per container? Did you find that was the best way to optimise performance? I have been trying to increase load but it is difficult to find the right number of containers and memory per container. – Alexandre Cassagne May 22 '20 at 03:46
  • 1
    What numbers in parentheses does mean? – Ruslan Stelmachenko Dec 08 '20 at 05:35
  • Sadly, The `xxxRAMPercentage` are still only affecting the heap, not the whole memory consumption of the JVM process (as soon from the OS/hypervisor) – Sebastian J. Dec 13 '21 at 02:15
21

The recent oracle-jdk-8(8u191) brings the following options to allow Docker container users to gain more fine grained control over the amount of system memory that will be used for the Java Heap:

-XX:InitialRAMPercentage
-XX:MaxRAMPercentage
-XX:MinRAMPercentage

Three new JVM options have been added to allow Docker container users to gain more fine grained control over the amount of system memory that will be used for the Java Heap:

-XX:InitialRAMPercentage -XX:MaxRAMPercentage -XX:MinRAMPercentage These options replace the deprecated Fraction forms (-XX:InitialRAMFraction, -XX:MaxRAMFraction, and -XX:MinRAMFraction).

See https://www.oracle.com/technetwork/java/javase/8u191-relnotes-5032181.html

a.l.
  • 1,085
  • 12
  • 29
  • 1
    These flags are available in JDK 10+ https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8186248 However, `-XX:+UseContainerSupport` is available now with `8u191`. – Fleshgrinder Jan 11 '19 at 14:08
  • @Fleshgrinder I write a simple demo to test it, and it works on jdk-8u191, see https://github.com/alex-lx/jdk-8-heap-test – a.l. Jan 12 '19 at 14:58
  • I tried with `-XX:MaxRAMPercentage=75` and it gave me an error, maybe the fraction is required?!? Also interesting is that the bug itself does not mention a fix as part of `8u191`. – Fleshgrinder Jan 14 '19 at 08:28
  • 1
    @Fleshgrinder, It might be unreasonable, but in my test, the percentage must be a float number like 75.0, and another point should be mentioned that `-XX:MaxRAMPercentage` and `-XX:MinRAMPercentage` should be used together. – a.l. Jan 14 '19 at 14:16
  • Since the 8u191's release note include this feature, I think maybe someone forgot to update the bug :-P. Yes, it's really confused. – a.l. Jan 14 '19 at 14:19
  • 1
    Seems kinda broken in `8u191` one can use it as expected in (at least) 11. The requirement to use both max and min always together also makes little sense. – Fleshgrinder Jan 15 '19 at 11:11
  • @a.l. `MaxRAMPercentage` and `MinRAMPercentage` are different things and do not influence each other. One counts towards devices with small physical memory and the other not. – Eugene Nov 15 '19 at 16:36
  • @Eugene I agree these options should be independency. However, I did a test which indicates they're not, it's too long to remember the details of the test, and I can't reproduce the expected right now :-(, so I guess I made a mistake in that test. – a.l. Nov 16 '19 at 10:59