Automatically configure Java to use the maximum RA

2019-04-08 04:34发布

问题:

If I do docker run --memory=X to run a Java app through a Bash script, is there any way to get this Bash script to reliably infer how much memory has been allocated to the container? Basically, I want this Bash script to do something like:

#!/bin/bash

# (Do some other stuff...)

MAX_RAM="$(get-max-ram)"
exec java "-Xms${MAX_RAM}" "-Xmx${MAX_RAM}" -jar my_jar.jar

Also, if I do the above, should I make Java use a little bit less than the maximum RAM?

回答1:

Docker uses cgroups to implement resource limits. Inside the container, you can use the cgget utility, to print parameters of various subsystems (in this instance, the memory subsystem).

For example, consider a container started with a memory limit of 64G (it's Java, after all):

> docker run --memory=64G

Within the container, then use cgget to read the current value of the memory.limit_in_bytes parameter:

> cgget -nvr memory.limit_in_bytes /
68719476736

Note that you'll probably have to install the cgget binary via the container image's package manager first. On Ubuntu, you'll need the cgroup-bin package.

You can then use this value to dynamically compute your JVM parameters (just as an example, adjust to your own needs):

MAX_RAM=$(cgget -nvr memory.limit_in_bytes /)
JVM_MIN_HEAP=$(printf "%.0f" $(echo "${MAX_RAM} * 0.2" | bc))
JVM_MAX_HEAP=$(printf "%.0f" $(echo "${MAX_RAM} * 0.8" | bc))
exec java "-Xms${JVM_MIN_HEAP}" "-Xmx${JVM_MAX_HEAP}" -jar my_jar.jar

Important: When run without memory restriction (i.e. without --memory=X flag), the memory.limit_in_bytes parameter will still have a value, although of the magnitude of around 2^63 - 4096:

> cgget -nvr memory.limit_in_bytes /
9223372036854771712

Unless you want to start your JVM with a minimum heap space of about 8 EB, your entrypoint script should also consider this case:

MAX_RAM=$(cgget -nvr memory.limit_in_bytes /)
if [ $MAX_RAM -le 137438953472 ] ; then
    JVM_MIN_HEAP=$(printf "%.0f" $(echo "${MAX_RAM} * 0.2" | bc))
    JVM_MAX_HEAP=$(printf "%.0f" $(echo "${MAX_RAM} * 0.8" | bc))
else
    JVM_MIN_HEAP=32G
    JVM_MAX_HEAP=128G
fi

exec java "-Xms${JVM_MIN_HEAP}" "-Xmx${JVM_MAX_HEAP}" -jar my_jar.jar


回答2:

  • java5/6/7/8u131-: a simple but efffective method, You can use java -Xmx cat /sys/fs/cgroup/memory/memory.limit_in_bytes (except windows)

  • java8u131+ and java9+: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap would do the job.

  • java 8u191+: UseContainerSupport is turned on by default. This feature is backported from java10, but it hasn't been backported to java9 so far.

  • java10+: UseContainerSupport is turned on by default.

Ref:

  • http://royvanrijn.com/blog/2018/05/java-and-docker-memory-limits/
  • https://blog.docker.com/2018/04/improved-docker-container-integration-with-java-10/
  • https://blogs.oracle.com/java-platform-group/java-se-support-for-docker-cpu-and-memory-limits
  • http://hg.openjdk.java.net/jdk/jdk/file/03f2bfdcb636/src/hotspot/os/linux/osContainer_linux.cpp


标签: java bash docker