The absolutely most basic advice is probably: "-Xmx" does not represent the actual upper limit for memory usage. We actually most often only set 50% of the assigend memory for the jvm.
Somebody mentioned https://github.com/cloudfoundry/java-buildpack-memory-calcul... which seems pretty interesting.
IIRC it was due to a bug in glibc >= 2.1. Something about how mallocs are pooled. IIRC you need to tune it to be <= num of physical threads. Usually people advise 4 or 2.
# openjdk 8 bug: HotSpot leaking memory in long-running requests
# workaround:
# - Disable HotSpot completely with -Xint
# - slows the memory growth, but does not halt it:
MALLOC_ARENA_MAX=4
So, ensure that your java process is launched with that environment var (so, export it in the same shell, or precede your java command with it).If you happen to be using Tomcat, I recommend putting:
export MALLOC_ARENA_MAX=4
into: /usr/local/tomcat/bin/setenv.sh
As for how much memory you allocate to your containers: as of JRE 8u131 you can make this far more container-friendly: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
This is equivalent to saying: -XX:MaxRAM=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)
https://github.com/moby/moby/issues/15020
https://github.com/docker-library/openjdk/issues/57
https://bugs.openjdk.java.net/browse/JDK-8170888In Spring Boot 2 I'm told you can can use functional beans to cut down on RAM usage. Not sure how it works.
But really, it comes down to deciding if you need a dependency or not.
As is normal for questions about Spring Boot performance, Dave Syer has done a lot of investigating: https://github.com/dsyer/spring-boot-memory-blog
You can also use smaller frameworks (Vertx? Javalin? possibly Spring Boot 2). I hope that with Java 9 we won't see this amount of memory usage anymore, however our organisation isn't there yet though.
In fact I would rather look at serverless architecture before considering docker/Kubernetes.
or, when you have a legacy app that relies on java 6, but you want everything else to run on java 8, the ability to drop everything into a container with its runtime is a life saver.
source: I'm the devops person that's responsible for making this work
If you want to be more clever, you could try this: https://github.com/cloudfoundry/java-buildpack-memory-calcul...
Sadly the article mentions very little in terms of practical advice. We've tried running some small Java 8 Spring Boot containers in Kubernetes which are configured to use max ~50M heap and ~150M total off-heap yet decide to use in excess of double that memory so we end up with either a lot of OOMKills or overly large memory limits.