diff --git a/images/alpine/openjdk8/jdk/README.md b/images/alpine/openjdk8/jdk/README.md index d9a9332..8746afd 100644 --- a/images/alpine/openjdk8/jdk/README.md +++ b/images/alpine/openjdk8/jdk/README.md @@ -96,10 +96,6 @@ The startup process is configured mostly via environment variables: * **JAVA_APP_DIR** the directory where the application resides. All paths in your application are relative to this directory. By default it is the same directory where this startup script resides. * **JAVA_LIB_DIR** directory holding the Java jar files as well an optional `classpath` file which holds the classpath. Either as a single line classpath (colon separated) or with jar files listed line-by-line. If not set **JAVA_LIB_DIR** is the same as **JAVA_APP_DIR**. * **JAVA_OPTIONS** options to add when calling `java` -* **JAVA_MAJOR_VERSION** a number >= 7. If the version is set then only options suitable for this version are used. When set to 7 options known only to Java > 8 will be removed. For versions >= 10 no explicit memory limit is calculated since Java >= 10 has support for container limits. -* **JAVA_MAX_MEM_RATIO** is used when no `-Xmx` option is given in `JAVA_OPTIONS`. This is used to calculate a default maximal Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio of the container available memory as set here. The default is `25` when the maximum amount of memory available to the container is below 300M, `50` otherwise, which means in that case that 50% of the available memory is used as an upper boundary. You can skip this mechanism by setting this value to 0 in which case no `-Xmx` option is added. -* **JAVA_INIT_MEM_RATIO** is used when no `-Xms` option is given in `JAVA_OPTIONS`. This is used to calculate a default initial Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xms` is set to a ratio of the container available memory as set here. By default this value is not set. -* **JAVA_MAX_CORE** restrict manually the number of cores available which is used for calculating certain defaults like the number of garbage collector threads. If set to 0 no base JVM tuning based on the number of cores is performed. * **JAVA_DIAGNOSTICS** set this to get some diagnostics information to standard out when things are happening * **JAVA_MAIN_CLASS** A main class to use as argument for `java`. When this environment variable is given, all jar files in `$JAVA_APP_DIR` are added to the classpath as well as `$JAVA_LIB_DIR`. * **JAVA_APP_JAR** A jar file with an appropriate manifest so that it can be started with `java -jar` if no `$JAVA_MAIN_CLASS` is set. In all cases this jar file is added to the classpath, too. @@ -131,11 +127,6 @@ These variables can be also set in a shell config file `run-env.sh`, which will This startup script also checks for a command `run-java-options`. If existent it will be called and the output is added to the environment variable `$JAVA_OPTIONS`. -The startup script also exposes some environment variables describing container limits which can be used by applications: - -* **CONTAINER_CORE_LIMIT** a calculated core limit as described in https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt -* **CONTAINER_MAX_MEMORY** memory limit given to the container - Any arguments given to the script are given through directly as argument to the Java application. Example: @@ -143,16 +134,12 @@ Example: ``` # Set the application directory directly export JAVA_APP_DIR=/deployments -# Set -Xmx based on container constraints -export JAVA_MAX_MEM_RATIO=40 # Start the jar in JAVA_APP_DIR with the given arguments ./run-java.sh --user maxmorlock --password secret ``` ### Options -This script can also be used to calculate reasonable, best-practice options for starting Java apps in general. For example, when running Maven in a container it makes sense to respect container Memory constraints. - The subcommand `options` can be used to print options to standard output so that is can be easily used to feed it to another, Java based application. When no extra arguments are given, all defaults will be used, which can be influenced with the environment variables described above. @@ -160,19 +147,15 @@ When no extra arguments are given, all defaults will be used, which can be influ You can select specific sets of options by providing additional arguments: * `--debug` : Java debug options if `JAVA_DEBUG` is set -* `--memory` : Memory settings based on the environment variables given * `--proxy` : Evaluate proxy environments variables -* `--cpu` : Tuning when the number of cores is limited -* `--gc` : GC tuning parameters -* `--jit` : JIT options * `--diagnostics` : Print diagnostics options when `JAVA_DIAGNOSTICS` is set -* `--java-default` : Same as `--memory --jit --diagnostic --cpu --gc` +* `--java-default` : Default options Example: ``` -# Call Maven with the proper memory settings when running in an container -export MAVEN_OPTS="$(run-java.sh options --memory)" +# Call Maven with the proper proxy settings when running in an container +export MAVEN_OPTS="$(run-java.sh options --proxy)" mvn clean install ``` diff --git a/images/alpine/openjdk8/jdk/run-java.sh b/images/alpine/openjdk8/jdk/run-java.sh index 1ebd872..23a973c 100644 --- a/images/alpine/openjdk8/jdk/run-java.sh +++ b/images/alpine/openjdk8/jdk/run-java.sh @@ -1,7 +1,8 @@ #!/bin/sh -# =================================================================================== -# Generic startup script for running arbitrary Java applications with -# being optimized for running in containers +# ============================================================================================================== +# Generic startup script for running arbitrary Java applications for JVM with support for -XX+UseContainerSupport +# and -XX:MaxRAMPercentage, -XX:MinRAMPercentage and -XX:InitialRAMPercentage +# for running in containers # # Usage: # # Execute a Java app: @@ -20,32 +21,16 @@ # Env-variables evaluated in this script: # # JAVA_OPTIONS: Checked for already set options -# JAVA_MAX_MEM_RATIO: Ratio use to calculate a default maximum Memory, in percent. -# E.g. the "50" value implies that 50% of the Memory -# given to the container is used as the maximum heap memory with -# '-Xmx'. -# It defaults to "25" when the maximum amount of memory available -# to the container is below 300M, otherwise defaults to "50". -# It is a heuristic and should be better backed up with real -# experiments and measurements. -# For a good overviews what tuning options are available --> -# https://youtu.be/Vt4G-pHXfs4 -# https://www.youtube.com/watch?v=w1rZOY5gbvk -# https://vimeo.com/album/4133413/video/181900266 -# Also note that heap is only a small portion of the memory used by a JVM. There are lot -# of other memory areas (metadata, thread, code cache, ...) which addes to the overall -# size. When your container gets killed because of an OOM, then you should tune -# the absolute values. -# JAVA_INIT_MEM_RATIO: Ratio use to calculate a default intial heap memory, in percent. -# By default this value is not set. -# -# The following variables are exposed to your Java application: -# -# CONTAINER_MAX_MEMORY: Max memory for the container (if running within a container) -# MAX_CORE_LIMIT: Number of cores available for the container (if running within a container) +# JVM values to set for heap size: +# -XX:MaxRAMPercentage +# -XX:MinRAMPercentage +# -XX:InitialRAMPercentage +# By default the values of Min and Max RAMPerdentage is set to 85.0 +# to allow the container's 85% of memory to be used for the max heap. +# Initial RAM Percentage is not set. -# ========================================================== +# ================================================================================================================= # Fail on a single failed command in a pipeline (if supported) (set -o | grep -q pipefail) && set -o pipefail @@ -122,71 +107,6 @@ find_jar_file() { fi } -# Generic formula evaluation based on awk -calc() { - local formula="$1" - shift - echo "$@" | awk ' - function ceil(x) { - return x % 1 ? int(x) + 1 : x - } - function log2(x) { - return log(x)/log(2) - } - function max2(x, y) { - return x > y ? x : y - } - function round(x) { - return int(x + 0.5) - } - {print '"int(${formula})"'} - ' -} - -# Based on the cgroup limits, figure out the max number of core we should utilize -core_limit() { - local cpu_period_file="/sys/fs/cgroup/cpu/cpu.cfs_period_us" - local cpu_quota_file="/sys/fs/cgroup/cpu/cpu.cfs_quota_us" - if [ -r "${cpu_period_file}" ]; then - local cpu_period="$(cat ${cpu_period_file})" - - if [ -r "${cpu_quota_file}" ]; then - local cpu_quota="$(cat ${cpu_quota_file})" - # cfs_quota_us == -1 --> no restrictions - if [ ${cpu_quota:-0} -ne -1 ]; then - echo $(calc 'ceil($1/$2)' "${cpu_quota}" "${cpu_period}") - fi - fi - fi -} - -max_memory() { - # High number which is the max limit until which memory is supposed to be - # unbounded. - local mem_file="/sys/fs/cgroup/memory/memory.limit_in_bytes" - if [ -r "${mem_file}" ]; then - local max_mem_cgroup="$(cat ${mem_file})" - local max_mem_meminfo_kb="$(cat /proc/meminfo | awk '/MemTotal/ {print $2}')" - local max_mem_meminfo="$(expr $max_mem_meminfo_kb \* 1024)" - if [ ${max_mem_cgroup:-0} != -1 ] && [ ${max_mem_cgroup:-0} -lt ${max_mem_meminfo:-0} ] - then - echo "${max_mem_cgroup}" - fi - fi -} - -init_limit_env_vars() { - # Read in container limits and export the as environment variables - local core_limit="$(core_limit)" - if [ -n "${core_limit}" ]; then - export CONTAINER_CORE_LIMIT="${core_limit}" - fi - - local mem_limit="$(max_memory)" - if [ -n "${mem_limit}" ]; then - export CONTAINER_MAX_MEMORY="${mem_limit}" - fi -} load_env() { local script_dir="$1" @@ -284,88 +204,31 @@ format_classpath() { # ========================================================================== memory_options() { - echo "$(calc_init_memory) $(calc_max_memory)" + echo "$(set_max_memory)" return } -# Check for memory options and set max heap size if needed -calc_max_memory() { +# Check for memory options and set max heap size as percentage of RAM, if needed +set_max_memory() { + local MaxRAMPercentage="" + local MinRAMPercentage="" + # Check whether -Xmx is already given in JAVA_OPTIONS if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xmx"; then return fi - - if [ -z "${CONTAINER_MAX_MEMORY:-}" ]; then - return - fi - - # Check for the 'real memory size' and calculate Xmx from the ratio - if [ -n "${JAVA_MAX_MEM_RATIO:-}" ]; then - if [ "${JAVA_MAX_MEM_RATIO}" -eq 0 ]; then - # Explicitely switched off - return - fi - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_MAX_MEM_RATIO}" "mx" - # When JAVA_MAX_MEM_RATIO not set and JVM >= 10 no max_memory - elif [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - elif [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - # Restore the one-fourth default heap size instead of the one-half below 300MB threshold - # See https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "25" "mx" - else - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "50" "mx" - fi -} - -# Check for memory options and set initial heap size if requested -calc_init_memory() { - # Check whether -Xms is already given in JAVA_OPTIONS. - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xms"; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MaxRAMPercentage"); then + MaxRAMPercentage="-XX:MaxRAMPercentage=85.0" fi - - # Check if value set - if [ -z "${JAVA_INIT_MEM_RATIO:-}" ] || [ -z "${CONTAINER_MAX_MEMORY:-}" ] || [ "${JAVA_INIT_MEM_RATIO}" -eq 0 ]; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MinRAMPercentage"); then + MinRAMPercentage="-XX:MinRAMPercentage=85.0" fi - # Calculate Xms from the ratio given - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_INIT_MEM_RATIO}" "ms" + echo "${MaxRAMPercentage} ${MinRAMPercentage}" } -calc_mem_opt() { - local max_mem="$1" - local fraction="$2" - local mem_opt="$3" - - local val=$(calc 'round($1*$2/100/1048576)' "${max_mem}" "${fraction}") - echo "-X${mem_opt}${val}m" -} - -c2_disabled() { - if [ -n "${CONTAINER_MAX_MEMORY:-}" ]; then - # Disable C2 compiler when container memory <=300MB - if [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - echo true - return - fi - fi - echo false -} - -jit_options() { - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - # Check whether -XX:TieredStopAtLevel is already given in JAVA_OPTIONS - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:TieredStopAtLevel"; then - return - fi - if [ $(c2_disabled) = true ]; then - echo "-XX:TieredStopAtLevel=1" - fi -} # Switch on diagnostics except when switched off diagnostics_options() { @@ -378,72 +241,9 @@ diagnostics_options() { fi } -# Replicate thread ergonomics for tiered compilation. -# This could ideally be skipped when tiered compilation is disabled. -# The algorithm is taken from: -# OpenJDK / jdk8u / jdk8u / hotspot -# src/share/vm/runtime/advancedThresholdPolicy.cpp -ci_compiler_count() { - local core_limit="$1" - local log_cpu=$(calc 'log2($1)' "$core_limit") - local loglog_cpu=$(calc 'log2(max2($1,1))' "$log_cpu") - local count=$(calc 'max2($1*$2,1)*3/2' "$log_cpu" "$loglog_cpu") - local c1_count=$(calc 'max2($1/3,1)' "$count") - local c2_count=$(calc 'max2($1-$2,1)' "$count" "$c1_count") - [ $(c2_disabled) = true ] && echo "$c1_count" || echo $(calc '$1+$2' "$c1_count" "$c2_count") -} - -cpu_options() { - # JVMs >= 10 know about CPU limits - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - - local core_limit="${JAVA_CORE_LIMIT:-}" - if [ "$core_limit" = "0" ]; then - return - fi - - if [ -n "${CONTAINER_CORE_LIMIT:-}" ]; then - if [ -z ${core_limit} ]; then - core_limit="${CONTAINER_CORE_LIMIT}" - fi - echo "-XX:ParallelGCThreads=${core_limit} " \ - "-XX:ConcGCThreads=${core_limit} " \ - "-Djava.util.concurrent.ForkJoinPool.common.parallelism=${core_limit} " \ - "-XX:CICompilerCount=$(ci_compiler_count $core_limit)" - fi -} - -#-XX:MinHeapFreeRatio=20 These parameters tell the heap to shrink aggressively and to grow conservatively. -#-XX:MaxHeapFreeRatio=40 Thereby optimizing the amount of memory available to the operating system. -heap_ratio() { - echo "-XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40" -} - -# These parameters are necessary when running parallel GC if you want to use the Min and Max Heap Free ratios. -# Skip setting gc_options if any other GC is set in JAVA_OPTIONS. -# -XX:GCTimeRatio=4 -# -XX:AdaptiveSizePolicyWeight=90 -gc_options() { - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:.*Use.*GC"; then - return - fi - - local opts="" - # for JVMs < 10 set GC settings - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-0}" -lt "10" ]; then - opts="${opts} -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 $(heap_ratio)" - fi - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-}" != "7" ]; then - opts="${opts} -XX:+ExitOnOutOfMemoryError" - fi - echo $opts -} - java_default_options() { # Echo options, trimming trailing and multiple spaces - echo "$(memory_options) $(jit_options) $(diagnostics_options) $(cpu_options) $(gc_options)" | awk '$1=$1' + echo "$(memory_options) $(diagnostics_options)" | awk '$1=$1' } @@ -564,21 +364,9 @@ options() { if [ $(hasflag --java-default) ]; then ret="$ret $(java_default_options)" fi - if [ $(hasflag --memory) ]; then - ret="$ret $(memory_options)" - fi - if [ $(hasflag --jit) ]; then - ret="$ret $(jit_options)" - fi if [ $(hasflag --diagnostics) ]; then ret="$ret $(diagnostics_options)" fi - if [ $(hasflag --cpu) ]; then - ret="$ret $(cpu_options)" - fi - if [ $(hasflag --gc) ]; then - ret="$ret $(gc_options)" - fi echo $ret | awk '$1=$1' } @@ -608,9 +396,6 @@ run() { # ============================================================================= # Fire up -# Set env vars reflecting limits -init_limit_env_vars - first_arg=${1:-} if [ "${first_arg}" = "options" ]; then # Print out options only @@ -622,4 +407,4 @@ elif [ "${first_arg}" = "run" ]; then # as first argument to your shift fi -run "$@" +run "$@" \ No newline at end of file diff --git a/images/alpine/openjdk8/jre/README.md b/images/alpine/openjdk8/jre/README.md index d8ae63f..4fd95ad 100644 --- a/images/alpine/openjdk8/jre/README.md +++ b/images/alpine/openjdk8/jre/README.md @@ -96,10 +96,6 @@ The startup process is configured mostly via environment variables: * **JAVA_APP_DIR** the directory where the application resides. All paths in your application are relative to this directory. By default it is the same directory where this startup script resides. * **JAVA_LIB_DIR** directory holding the Java jar files as well an optional `classpath` file which holds the classpath. Either as a single line classpath (colon separated) or with jar files listed line-by-line. If not set **JAVA_LIB_DIR** is the same as **JAVA_APP_DIR**. * **JAVA_OPTIONS** options to add when calling `java` -* **JAVA_MAJOR_VERSION** a number >= 7. If the version is set then only options suitable for this version are used. When set to 7 options known only to Java > 8 will be removed. For versions >= 10 no explicit memory limit is calculated since Java >= 10 has support for container limits. -* **JAVA_MAX_MEM_RATIO** is used when no `-Xmx` option is given in `JAVA_OPTIONS`. This is used to calculate a default maximal Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio of the container available memory as set here. The default is `25` when the maximum amount of memory available to the container is below 300M, `50` otherwise, which means in that case that 50% of the available memory is used as an upper boundary. You can skip this mechanism by setting this value to 0 in which case no `-Xmx` option is added. -* **JAVA_INIT_MEM_RATIO** is used when no `-Xms` option is given in `JAVA_OPTIONS`. This is used to calculate a default initial Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xms` is set to a ratio of the container available memory as set here. By default this value is not set. -* **JAVA_MAX_CORE** restrict manually the number of cores available which is used for calculating certain defaults like the number of garbage collector threads. If set to 0 no base JVM tuning based on the number of cores is performed. * **JAVA_DIAGNOSTICS** set this to get some diagnostics information to standard out when things are happening * **JAVA_MAIN_CLASS** A main class to use as argument for `java`. When this environment variable is given, all jar files in `$JAVA_APP_DIR` are added to the classpath as well as `$JAVA_LIB_DIR`. * **JAVA_APP_JAR** A jar file with an appropriate manifest so that it can be started with `java -jar` if no `$JAVA_MAIN_CLASS` is set. In all cases this jar file is added to the classpath, too. @@ -133,8 +129,6 @@ This startup script also checks for a command `run-java-options`. If existent it The startup script also exposes some environment variables describing container limits which can be used by applications: -* **CONTAINER_CORE_LIMIT** a calculated core limit as described in https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt -* **CONTAINER_MAX_MEMORY** memory limit given to the container Any arguments given to the script are given through directly as argument to the Java application. @@ -143,16 +137,12 @@ Example: ``` # Set the application directory directly export JAVA_APP_DIR=/deployments -# Set -Xmx based on container constraints -export JAVA_MAX_MEM_RATIO=40 # Start the jar in JAVA_APP_DIR with the given arguments ./run-java.sh --user maxmorlock --password secret ``` ### Options -This script can also be used to calculate reasonable, best-practice options for starting Java apps in general. For example, when running Maven in a container it makes sense to respect container Memory constraints. - The subcommand `options` can be used to print options to standard output so that is can be easily used to feed it to another, Java based application. When no extra arguments are given, all defaults will be used, which can be influenced with the environment variables described above. @@ -160,19 +150,15 @@ When no extra arguments are given, all defaults will be used, which can be influ You can select specific sets of options by providing additional arguments: * `--debug` : Java debug options if `JAVA_DEBUG` is set -* `--memory` : Memory settings based on the environment variables given * `--proxy` : Evaluate proxy environments variables -* `--cpu` : Tuning when the number of cores is limited -* `--gc` : GC tuning parameters -* `--jit` : JIT options * `--diagnostics` : Print diagnostics options when `JAVA_DIAGNOSTICS` is set -* `--java-default` : Same as `--memory --jit --diagnostic --cpu --gc` +* `--java-default` : Default options Example: ``` -# Call Maven with the proper memory settings when running in an container -export MAVEN_OPTS="$(run-java.sh options --memory)" +# Call Maven with the proper proxy settings when running in an container +export MAVEN_OPTS="$(run-java.sh options --proxy)" mvn clean install ``` diff --git a/images/alpine/openjdk8/jre/run-java.sh b/images/alpine/openjdk8/jre/run-java.sh index 1ebd872..23a973c 100644 --- a/images/alpine/openjdk8/jre/run-java.sh +++ b/images/alpine/openjdk8/jre/run-java.sh @@ -1,7 +1,8 @@ #!/bin/sh -# =================================================================================== -# Generic startup script for running arbitrary Java applications with -# being optimized for running in containers +# ============================================================================================================== +# Generic startup script for running arbitrary Java applications for JVM with support for -XX+UseContainerSupport +# and -XX:MaxRAMPercentage, -XX:MinRAMPercentage and -XX:InitialRAMPercentage +# for running in containers # # Usage: # # Execute a Java app: @@ -20,32 +21,16 @@ # Env-variables evaluated in this script: # # JAVA_OPTIONS: Checked for already set options -# JAVA_MAX_MEM_RATIO: Ratio use to calculate a default maximum Memory, in percent. -# E.g. the "50" value implies that 50% of the Memory -# given to the container is used as the maximum heap memory with -# '-Xmx'. -# It defaults to "25" when the maximum amount of memory available -# to the container is below 300M, otherwise defaults to "50". -# It is a heuristic and should be better backed up with real -# experiments and measurements. -# For a good overviews what tuning options are available --> -# https://youtu.be/Vt4G-pHXfs4 -# https://www.youtube.com/watch?v=w1rZOY5gbvk -# https://vimeo.com/album/4133413/video/181900266 -# Also note that heap is only a small portion of the memory used by a JVM. There are lot -# of other memory areas (metadata, thread, code cache, ...) which addes to the overall -# size. When your container gets killed because of an OOM, then you should tune -# the absolute values. -# JAVA_INIT_MEM_RATIO: Ratio use to calculate a default intial heap memory, in percent. -# By default this value is not set. -# -# The following variables are exposed to your Java application: -# -# CONTAINER_MAX_MEMORY: Max memory for the container (if running within a container) -# MAX_CORE_LIMIT: Number of cores available for the container (if running within a container) +# JVM values to set for heap size: +# -XX:MaxRAMPercentage +# -XX:MinRAMPercentage +# -XX:InitialRAMPercentage +# By default the values of Min and Max RAMPerdentage is set to 85.0 +# to allow the container's 85% of memory to be used for the max heap. +# Initial RAM Percentage is not set. -# ========================================================== +# ================================================================================================================= # Fail on a single failed command in a pipeline (if supported) (set -o | grep -q pipefail) && set -o pipefail @@ -122,71 +107,6 @@ find_jar_file() { fi } -# Generic formula evaluation based on awk -calc() { - local formula="$1" - shift - echo "$@" | awk ' - function ceil(x) { - return x % 1 ? int(x) + 1 : x - } - function log2(x) { - return log(x)/log(2) - } - function max2(x, y) { - return x > y ? x : y - } - function round(x) { - return int(x + 0.5) - } - {print '"int(${formula})"'} - ' -} - -# Based on the cgroup limits, figure out the max number of core we should utilize -core_limit() { - local cpu_period_file="/sys/fs/cgroup/cpu/cpu.cfs_period_us" - local cpu_quota_file="/sys/fs/cgroup/cpu/cpu.cfs_quota_us" - if [ -r "${cpu_period_file}" ]; then - local cpu_period="$(cat ${cpu_period_file})" - - if [ -r "${cpu_quota_file}" ]; then - local cpu_quota="$(cat ${cpu_quota_file})" - # cfs_quota_us == -1 --> no restrictions - if [ ${cpu_quota:-0} -ne -1 ]; then - echo $(calc 'ceil($1/$2)' "${cpu_quota}" "${cpu_period}") - fi - fi - fi -} - -max_memory() { - # High number which is the max limit until which memory is supposed to be - # unbounded. - local mem_file="/sys/fs/cgroup/memory/memory.limit_in_bytes" - if [ -r "${mem_file}" ]; then - local max_mem_cgroup="$(cat ${mem_file})" - local max_mem_meminfo_kb="$(cat /proc/meminfo | awk '/MemTotal/ {print $2}')" - local max_mem_meminfo="$(expr $max_mem_meminfo_kb \* 1024)" - if [ ${max_mem_cgroup:-0} != -1 ] && [ ${max_mem_cgroup:-0} -lt ${max_mem_meminfo:-0} ] - then - echo "${max_mem_cgroup}" - fi - fi -} - -init_limit_env_vars() { - # Read in container limits and export the as environment variables - local core_limit="$(core_limit)" - if [ -n "${core_limit}" ]; then - export CONTAINER_CORE_LIMIT="${core_limit}" - fi - - local mem_limit="$(max_memory)" - if [ -n "${mem_limit}" ]; then - export CONTAINER_MAX_MEMORY="${mem_limit}" - fi -} load_env() { local script_dir="$1" @@ -284,88 +204,31 @@ format_classpath() { # ========================================================================== memory_options() { - echo "$(calc_init_memory) $(calc_max_memory)" + echo "$(set_max_memory)" return } -# Check for memory options and set max heap size if needed -calc_max_memory() { +# Check for memory options and set max heap size as percentage of RAM, if needed +set_max_memory() { + local MaxRAMPercentage="" + local MinRAMPercentage="" + # Check whether -Xmx is already given in JAVA_OPTIONS if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xmx"; then return fi - - if [ -z "${CONTAINER_MAX_MEMORY:-}" ]; then - return - fi - - # Check for the 'real memory size' and calculate Xmx from the ratio - if [ -n "${JAVA_MAX_MEM_RATIO:-}" ]; then - if [ "${JAVA_MAX_MEM_RATIO}" -eq 0 ]; then - # Explicitely switched off - return - fi - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_MAX_MEM_RATIO}" "mx" - # When JAVA_MAX_MEM_RATIO not set and JVM >= 10 no max_memory - elif [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - elif [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - # Restore the one-fourth default heap size instead of the one-half below 300MB threshold - # See https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "25" "mx" - else - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "50" "mx" - fi -} - -# Check for memory options and set initial heap size if requested -calc_init_memory() { - # Check whether -Xms is already given in JAVA_OPTIONS. - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xms"; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MaxRAMPercentage"); then + MaxRAMPercentage="-XX:MaxRAMPercentage=85.0" fi - - # Check if value set - if [ -z "${JAVA_INIT_MEM_RATIO:-}" ] || [ -z "${CONTAINER_MAX_MEMORY:-}" ] || [ "${JAVA_INIT_MEM_RATIO}" -eq 0 ]; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MinRAMPercentage"); then + MinRAMPercentage="-XX:MinRAMPercentage=85.0" fi - # Calculate Xms from the ratio given - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_INIT_MEM_RATIO}" "ms" + echo "${MaxRAMPercentage} ${MinRAMPercentage}" } -calc_mem_opt() { - local max_mem="$1" - local fraction="$2" - local mem_opt="$3" - - local val=$(calc 'round($1*$2/100/1048576)' "${max_mem}" "${fraction}") - echo "-X${mem_opt}${val}m" -} - -c2_disabled() { - if [ -n "${CONTAINER_MAX_MEMORY:-}" ]; then - # Disable C2 compiler when container memory <=300MB - if [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - echo true - return - fi - fi - echo false -} - -jit_options() { - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - # Check whether -XX:TieredStopAtLevel is already given in JAVA_OPTIONS - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:TieredStopAtLevel"; then - return - fi - if [ $(c2_disabled) = true ]; then - echo "-XX:TieredStopAtLevel=1" - fi -} # Switch on diagnostics except when switched off diagnostics_options() { @@ -378,72 +241,9 @@ diagnostics_options() { fi } -# Replicate thread ergonomics for tiered compilation. -# This could ideally be skipped when tiered compilation is disabled. -# The algorithm is taken from: -# OpenJDK / jdk8u / jdk8u / hotspot -# src/share/vm/runtime/advancedThresholdPolicy.cpp -ci_compiler_count() { - local core_limit="$1" - local log_cpu=$(calc 'log2($1)' "$core_limit") - local loglog_cpu=$(calc 'log2(max2($1,1))' "$log_cpu") - local count=$(calc 'max2($1*$2,1)*3/2' "$log_cpu" "$loglog_cpu") - local c1_count=$(calc 'max2($1/3,1)' "$count") - local c2_count=$(calc 'max2($1-$2,1)' "$count" "$c1_count") - [ $(c2_disabled) = true ] && echo "$c1_count" || echo $(calc '$1+$2' "$c1_count" "$c2_count") -} - -cpu_options() { - # JVMs >= 10 know about CPU limits - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - - local core_limit="${JAVA_CORE_LIMIT:-}" - if [ "$core_limit" = "0" ]; then - return - fi - - if [ -n "${CONTAINER_CORE_LIMIT:-}" ]; then - if [ -z ${core_limit} ]; then - core_limit="${CONTAINER_CORE_LIMIT}" - fi - echo "-XX:ParallelGCThreads=${core_limit} " \ - "-XX:ConcGCThreads=${core_limit} " \ - "-Djava.util.concurrent.ForkJoinPool.common.parallelism=${core_limit} " \ - "-XX:CICompilerCount=$(ci_compiler_count $core_limit)" - fi -} - -#-XX:MinHeapFreeRatio=20 These parameters tell the heap to shrink aggressively and to grow conservatively. -#-XX:MaxHeapFreeRatio=40 Thereby optimizing the amount of memory available to the operating system. -heap_ratio() { - echo "-XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40" -} - -# These parameters are necessary when running parallel GC if you want to use the Min and Max Heap Free ratios. -# Skip setting gc_options if any other GC is set in JAVA_OPTIONS. -# -XX:GCTimeRatio=4 -# -XX:AdaptiveSizePolicyWeight=90 -gc_options() { - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:.*Use.*GC"; then - return - fi - - local opts="" - # for JVMs < 10 set GC settings - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-0}" -lt "10" ]; then - opts="${opts} -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 $(heap_ratio)" - fi - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-}" != "7" ]; then - opts="${opts} -XX:+ExitOnOutOfMemoryError" - fi - echo $opts -} - java_default_options() { # Echo options, trimming trailing and multiple spaces - echo "$(memory_options) $(jit_options) $(diagnostics_options) $(cpu_options) $(gc_options)" | awk '$1=$1' + echo "$(memory_options) $(diagnostics_options)" | awk '$1=$1' } @@ -564,21 +364,9 @@ options() { if [ $(hasflag --java-default) ]; then ret="$ret $(java_default_options)" fi - if [ $(hasflag --memory) ]; then - ret="$ret $(memory_options)" - fi - if [ $(hasflag --jit) ]; then - ret="$ret $(jit_options)" - fi if [ $(hasflag --diagnostics) ]; then ret="$ret $(diagnostics_options)" fi - if [ $(hasflag --cpu) ]; then - ret="$ret $(cpu_options)" - fi - if [ $(hasflag --gc) ]; then - ret="$ret $(gc_options)" - fi echo $ret | awk '$1=$1' } @@ -608,9 +396,6 @@ run() { # ============================================================================= # Fire up -# Set env vars reflecting limits -init_limit_env_vars - first_arg=${1:-} if [ "${first_arg}" = "options" ]; then # Print out options only @@ -622,4 +407,4 @@ elif [ "${first_arg}" = "run" ]; then # as first argument to your shift fi -run "$@" +run "$@" \ No newline at end of file diff --git a/images/centos/openjdk11/jdk/README.md b/images/centos/openjdk11/jdk/README.md index bb247a2..afcdde2 100644 --- a/images/centos/openjdk11/jdk/README.md +++ b/images/centos/openjdk11/jdk/README.md @@ -96,10 +96,6 @@ The startup process is configured mostly via environment variables: * **JAVA_APP_DIR** the directory where the application resides. All paths in your application are relative to this directory. By default it is the same directory where this startup script resides. * **JAVA_LIB_DIR** directory holding the Java jar files as well an optional `classpath` file which holds the classpath. Either as a single line classpath (colon separated) or with jar files listed line-by-line. If not set **JAVA_LIB_DIR** is the same as **JAVA_APP_DIR**. * **JAVA_OPTIONS** options to add when calling `java` -* **JAVA_MAJOR_VERSION** a number >= 7. If the version is set then only options suitable for this version are used. When set to 7 options known only to Java > 8 will be removed. For versions >= 10 no explicit memory limit is calculated since Java >= 10 has support for container limits. -* **JAVA_MAX_MEM_RATIO** is used when no `-Xmx` option is given in `JAVA_OPTIONS`. This is used to calculate a default maximal Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio of the container available memory as set here. The default is `25` when the maximum amount of memory available to the container is below 300M, `50` otherwise, which means in that case that 50% of the available memory is used as an upper boundary. You can skip this mechanism by setting this value to 0 in which case no `-Xmx` option is added. -* **JAVA_INIT_MEM_RATIO** is used when no `-Xms` option is given in `JAVA_OPTIONS`. This is used to calculate a default initial Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xms` is set to a ratio of the container available memory as set here. By default this value is not set. -* **JAVA_MAX_CORE** restrict manually the number of cores available which is used for calculating certain defaults like the number of garbage collector threads. If set to 0 no base JVM tuning based on the number of cores is performed. * **JAVA_DIAGNOSTICS** set this to get some diagnostics information to standard out when things are happening * **JAVA_MAIN_CLASS** A main class to use as argument for `java`. When this environment variable is given, all jar files in `$JAVA_APP_DIR` are added to the classpath as well as `$JAVA_LIB_DIR`. * **JAVA_APP_JAR** A jar file with an appropriate manifest so that it can be started with `java -jar` if no `$JAVA_MAIN_CLASS` is set. In all cases this jar file is added to the classpath, too. @@ -131,10 +127,6 @@ These variables can be also set in a shell config file `run-env.sh`, which will This startup script also checks for a command `run-java-options`. If existent it will be called and the output is added to the environment variable `$JAVA_OPTIONS`. -The startup script also exposes some environment variables describing container limits which can be used by applications: - -* **CONTAINER_CORE_LIMIT** a calculated core limit as described in https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt -* **CONTAINER_MAX_MEMORY** memory limit given to the container Any arguments given to the script are given through directly as argument to the Java application. @@ -143,16 +135,12 @@ Example: ``` # Set the application directory directly export JAVA_APP_DIR=/deployments -# Set -Xmx based on container constraints -export JAVA_MAX_MEM_RATIO=40 # Start the jar in JAVA_APP_DIR with the given arguments ./run-java.sh --user maxmorlock --password secret ``` ### Options -This script can also be used to calculate reasonable, best-practice options for starting Java apps in general. For example, when running Maven in a container it makes sense to respect container Memory constraints. - The subcommand `options` can be used to print options to standard output so that is can be easily used to feed it to another, Java based application. When no extra arguments are given, all defaults will be used, which can be influenced with the environment variables described above. @@ -160,19 +148,15 @@ When no extra arguments are given, all defaults will be used, which can be influ You can select specific sets of options by providing additional arguments: * `--debug` : Java debug options if `JAVA_DEBUG` is set -* `--memory` : Memory settings based on the environment variables given * `--proxy` : Evaluate proxy environments variables -* `--cpu` : Tuning when the number of cores is limited -* `--gc` : GC tuning parameters -* `--jit` : JIT options * `--diagnostics` : Print diagnostics options when `JAVA_DIAGNOSTICS` is set -* `--java-default` : Same as `--memory --jit --diagnostic --cpu --gc` +* `--java-default` : Defaults Example: ``` -# Call Maven with the proper memory settings when running in an container -export MAVEN_OPTS="$(run-java.sh options --memory)" +# Call Maven with the proper proxy settings when running in an container +export MAVEN_OPTS="$(run-java.sh options --proxy)" mvn clean install ``` diff --git a/images/centos/openjdk11/jdk/run-java.sh b/images/centos/openjdk11/jdk/run-java.sh index 1ebd872..23a973c 100644 --- a/images/centos/openjdk11/jdk/run-java.sh +++ b/images/centos/openjdk11/jdk/run-java.sh @@ -1,7 +1,8 @@ #!/bin/sh -# =================================================================================== -# Generic startup script for running arbitrary Java applications with -# being optimized for running in containers +# ============================================================================================================== +# Generic startup script for running arbitrary Java applications for JVM with support for -XX+UseContainerSupport +# and -XX:MaxRAMPercentage, -XX:MinRAMPercentage and -XX:InitialRAMPercentage +# for running in containers # # Usage: # # Execute a Java app: @@ -20,32 +21,16 @@ # Env-variables evaluated in this script: # # JAVA_OPTIONS: Checked for already set options -# JAVA_MAX_MEM_RATIO: Ratio use to calculate a default maximum Memory, in percent. -# E.g. the "50" value implies that 50% of the Memory -# given to the container is used as the maximum heap memory with -# '-Xmx'. -# It defaults to "25" when the maximum amount of memory available -# to the container is below 300M, otherwise defaults to "50". -# It is a heuristic and should be better backed up with real -# experiments and measurements. -# For a good overviews what tuning options are available --> -# https://youtu.be/Vt4G-pHXfs4 -# https://www.youtube.com/watch?v=w1rZOY5gbvk -# https://vimeo.com/album/4133413/video/181900266 -# Also note that heap is only a small portion of the memory used by a JVM. There are lot -# of other memory areas (metadata, thread, code cache, ...) which addes to the overall -# size. When your container gets killed because of an OOM, then you should tune -# the absolute values. -# JAVA_INIT_MEM_RATIO: Ratio use to calculate a default intial heap memory, in percent. -# By default this value is not set. -# -# The following variables are exposed to your Java application: -# -# CONTAINER_MAX_MEMORY: Max memory for the container (if running within a container) -# MAX_CORE_LIMIT: Number of cores available for the container (if running within a container) +# JVM values to set for heap size: +# -XX:MaxRAMPercentage +# -XX:MinRAMPercentage +# -XX:InitialRAMPercentage +# By default the values of Min and Max RAMPerdentage is set to 85.0 +# to allow the container's 85% of memory to be used for the max heap. +# Initial RAM Percentage is not set. -# ========================================================== +# ================================================================================================================= # Fail on a single failed command in a pipeline (if supported) (set -o | grep -q pipefail) && set -o pipefail @@ -122,71 +107,6 @@ find_jar_file() { fi } -# Generic formula evaluation based on awk -calc() { - local formula="$1" - shift - echo "$@" | awk ' - function ceil(x) { - return x % 1 ? int(x) + 1 : x - } - function log2(x) { - return log(x)/log(2) - } - function max2(x, y) { - return x > y ? x : y - } - function round(x) { - return int(x + 0.5) - } - {print '"int(${formula})"'} - ' -} - -# Based on the cgroup limits, figure out the max number of core we should utilize -core_limit() { - local cpu_period_file="/sys/fs/cgroup/cpu/cpu.cfs_period_us" - local cpu_quota_file="/sys/fs/cgroup/cpu/cpu.cfs_quota_us" - if [ -r "${cpu_period_file}" ]; then - local cpu_period="$(cat ${cpu_period_file})" - - if [ -r "${cpu_quota_file}" ]; then - local cpu_quota="$(cat ${cpu_quota_file})" - # cfs_quota_us == -1 --> no restrictions - if [ ${cpu_quota:-0} -ne -1 ]; then - echo $(calc 'ceil($1/$2)' "${cpu_quota}" "${cpu_period}") - fi - fi - fi -} - -max_memory() { - # High number which is the max limit until which memory is supposed to be - # unbounded. - local mem_file="/sys/fs/cgroup/memory/memory.limit_in_bytes" - if [ -r "${mem_file}" ]; then - local max_mem_cgroup="$(cat ${mem_file})" - local max_mem_meminfo_kb="$(cat /proc/meminfo | awk '/MemTotal/ {print $2}')" - local max_mem_meminfo="$(expr $max_mem_meminfo_kb \* 1024)" - if [ ${max_mem_cgroup:-0} != -1 ] && [ ${max_mem_cgroup:-0} -lt ${max_mem_meminfo:-0} ] - then - echo "${max_mem_cgroup}" - fi - fi -} - -init_limit_env_vars() { - # Read in container limits and export the as environment variables - local core_limit="$(core_limit)" - if [ -n "${core_limit}" ]; then - export CONTAINER_CORE_LIMIT="${core_limit}" - fi - - local mem_limit="$(max_memory)" - if [ -n "${mem_limit}" ]; then - export CONTAINER_MAX_MEMORY="${mem_limit}" - fi -} load_env() { local script_dir="$1" @@ -284,88 +204,31 @@ format_classpath() { # ========================================================================== memory_options() { - echo "$(calc_init_memory) $(calc_max_memory)" + echo "$(set_max_memory)" return } -# Check for memory options and set max heap size if needed -calc_max_memory() { +# Check for memory options and set max heap size as percentage of RAM, if needed +set_max_memory() { + local MaxRAMPercentage="" + local MinRAMPercentage="" + # Check whether -Xmx is already given in JAVA_OPTIONS if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xmx"; then return fi - - if [ -z "${CONTAINER_MAX_MEMORY:-}" ]; then - return - fi - - # Check for the 'real memory size' and calculate Xmx from the ratio - if [ -n "${JAVA_MAX_MEM_RATIO:-}" ]; then - if [ "${JAVA_MAX_MEM_RATIO}" -eq 0 ]; then - # Explicitely switched off - return - fi - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_MAX_MEM_RATIO}" "mx" - # When JAVA_MAX_MEM_RATIO not set and JVM >= 10 no max_memory - elif [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - elif [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - # Restore the one-fourth default heap size instead of the one-half below 300MB threshold - # See https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "25" "mx" - else - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "50" "mx" - fi -} - -# Check for memory options and set initial heap size if requested -calc_init_memory() { - # Check whether -Xms is already given in JAVA_OPTIONS. - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xms"; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MaxRAMPercentage"); then + MaxRAMPercentage="-XX:MaxRAMPercentage=85.0" fi - - # Check if value set - if [ -z "${JAVA_INIT_MEM_RATIO:-}" ] || [ -z "${CONTAINER_MAX_MEMORY:-}" ] || [ "${JAVA_INIT_MEM_RATIO}" -eq 0 ]; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MinRAMPercentage"); then + MinRAMPercentage="-XX:MinRAMPercentage=85.0" fi - # Calculate Xms from the ratio given - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_INIT_MEM_RATIO}" "ms" + echo "${MaxRAMPercentage} ${MinRAMPercentage}" } -calc_mem_opt() { - local max_mem="$1" - local fraction="$2" - local mem_opt="$3" - - local val=$(calc 'round($1*$2/100/1048576)' "${max_mem}" "${fraction}") - echo "-X${mem_opt}${val}m" -} - -c2_disabled() { - if [ -n "${CONTAINER_MAX_MEMORY:-}" ]; then - # Disable C2 compiler when container memory <=300MB - if [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - echo true - return - fi - fi - echo false -} - -jit_options() { - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - # Check whether -XX:TieredStopAtLevel is already given in JAVA_OPTIONS - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:TieredStopAtLevel"; then - return - fi - if [ $(c2_disabled) = true ]; then - echo "-XX:TieredStopAtLevel=1" - fi -} # Switch on diagnostics except when switched off diagnostics_options() { @@ -378,72 +241,9 @@ diagnostics_options() { fi } -# Replicate thread ergonomics for tiered compilation. -# This could ideally be skipped when tiered compilation is disabled. -# The algorithm is taken from: -# OpenJDK / jdk8u / jdk8u / hotspot -# src/share/vm/runtime/advancedThresholdPolicy.cpp -ci_compiler_count() { - local core_limit="$1" - local log_cpu=$(calc 'log2($1)' "$core_limit") - local loglog_cpu=$(calc 'log2(max2($1,1))' "$log_cpu") - local count=$(calc 'max2($1*$2,1)*3/2' "$log_cpu" "$loglog_cpu") - local c1_count=$(calc 'max2($1/3,1)' "$count") - local c2_count=$(calc 'max2($1-$2,1)' "$count" "$c1_count") - [ $(c2_disabled) = true ] && echo "$c1_count" || echo $(calc '$1+$2' "$c1_count" "$c2_count") -} - -cpu_options() { - # JVMs >= 10 know about CPU limits - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - - local core_limit="${JAVA_CORE_LIMIT:-}" - if [ "$core_limit" = "0" ]; then - return - fi - - if [ -n "${CONTAINER_CORE_LIMIT:-}" ]; then - if [ -z ${core_limit} ]; then - core_limit="${CONTAINER_CORE_LIMIT}" - fi - echo "-XX:ParallelGCThreads=${core_limit} " \ - "-XX:ConcGCThreads=${core_limit} " \ - "-Djava.util.concurrent.ForkJoinPool.common.parallelism=${core_limit} " \ - "-XX:CICompilerCount=$(ci_compiler_count $core_limit)" - fi -} - -#-XX:MinHeapFreeRatio=20 These parameters tell the heap to shrink aggressively and to grow conservatively. -#-XX:MaxHeapFreeRatio=40 Thereby optimizing the amount of memory available to the operating system. -heap_ratio() { - echo "-XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40" -} - -# These parameters are necessary when running parallel GC if you want to use the Min and Max Heap Free ratios. -# Skip setting gc_options if any other GC is set in JAVA_OPTIONS. -# -XX:GCTimeRatio=4 -# -XX:AdaptiveSizePolicyWeight=90 -gc_options() { - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:.*Use.*GC"; then - return - fi - - local opts="" - # for JVMs < 10 set GC settings - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-0}" -lt "10" ]; then - opts="${opts} -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 $(heap_ratio)" - fi - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-}" != "7" ]; then - opts="${opts} -XX:+ExitOnOutOfMemoryError" - fi - echo $opts -} - java_default_options() { # Echo options, trimming trailing and multiple spaces - echo "$(memory_options) $(jit_options) $(diagnostics_options) $(cpu_options) $(gc_options)" | awk '$1=$1' + echo "$(memory_options) $(diagnostics_options)" | awk '$1=$1' } @@ -564,21 +364,9 @@ options() { if [ $(hasflag --java-default) ]; then ret="$ret $(java_default_options)" fi - if [ $(hasflag --memory) ]; then - ret="$ret $(memory_options)" - fi - if [ $(hasflag --jit) ]; then - ret="$ret $(jit_options)" - fi if [ $(hasflag --diagnostics) ]; then ret="$ret $(diagnostics_options)" fi - if [ $(hasflag --cpu) ]; then - ret="$ret $(cpu_options)" - fi - if [ $(hasflag --gc) ]; then - ret="$ret $(gc_options)" - fi echo $ret | awk '$1=$1' } @@ -608,9 +396,6 @@ run() { # ============================================================================= # Fire up -# Set env vars reflecting limits -init_limit_env_vars - first_arg=${1:-} if [ "${first_arg}" = "options" ]; then # Print out options only @@ -622,4 +407,4 @@ elif [ "${first_arg}" = "run" ]; then # as first argument to your shift fi -run "$@" +run "$@" \ No newline at end of file diff --git a/images/centos/openjdk11/jre/README.md b/images/centos/openjdk11/jre/README.md index 188316e..ee32cfb 100644 --- a/images/centos/openjdk11/jre/README.md +++ b/images/centos/openjdk11/jre/README.md @@ -96,10 +96,6 @@ The startup process is configured mostly via environment variables: * **JAVA_APP_DIR** the directory where the application resides. All paths in your application are relative to this directory. By default it is the same directory where this startup script resides. * **JAVA_LIB_DIR** directory holding the Java jar files as well an optional `classpath` file which holds the classpath. Either as a single line classpath (colon separated) or with jar files listed line-by-line. If not set **JAVA_LIB_DIR** is the same as **JAVA_APP_DIR**. * **JAVA_OPTIONS** options to add when calling `java` -* **JAVA_MAJOR_VERSION** a number >= 7. If the version is set then only options suitable for this version are used. When set to 7 options known only to Java > 8 will be removed. For versions >= 10 no explicit memory limit is calculated since Java >= 10 has support for container limits. -* **JAVA_MAX_MEM_RATIO** is used when no `-Xmx` option is given in `JAVA_OPTIONS`. This is used to calculate a default maximal Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio of the container available memory as set here. The default is `25` when the maximum amount of memory available to the container is below 300M, `50` otherwise, which means in that case that 50% of the available memory is used as an upper boundary. You can skip this mechanism by setting this value to 0 in which case no `-Xmx` option is added. -* **JAVA_INIT_MEM_RATIO** is used when no `-Xms` option is given in `JAVA_OPTIONS`. This is used to calculate a default initial Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xms` is set to a ratio of the container available memory as set here. By default this value is not set. -* **JAVA_MAX_CORE** restrict manually the number of cores available which is used for calculating certain defaults like the number of garbage collector threads. If set to 0 no base JVM tuning based on the number of cores is performed. * **JAVA_DIAGNOSTICS** set this to get some diagnostics information to standard out when things are happening * **JAVA_MAIN_CLASS** A main class to use as argument for `java`. When this environment variable is given, all jar files in `$JAVA_APP_DIR` are added to the classpath as well as `$JAVA_LIB_DIR`. * **JAVA_APP_JAR** A jar file with an appropriate manifest so that it can be started with `java -jar` if no `$JAVA_MAIN_CLASS` is set. In all cases this jar file is added to the classpath, too. @@ -131,11 +127,6 @@ These variables can be also set in a shell config file `run-env.sh`, which will This startup script also checks for a command `run-java-options`. If existent it will be called and the output is added to the environment variable `$JAVA_OPTIONS`. -The startup script also exposes some environment variables describing container limits which can be used by applications: - -* **CONTAINER_CORE_LIMIT** a calculated core limit as described in https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt -* **CONTAINER_MAX_MEMORY** memory limit given to the container - Any arguments given to the script are given through directly as argument to the Java application. Example: @@ -143,16 +134,12 @@ Example: ``` # Set the application directory directly export JAVA_APP_DIR=/deployments -# Set -Xmx based on container constraints -export JAVA_MAX_MEM_RATIO=40 # Start the jar in JAVA_APP_DIR with the given arguments ./run-java.sh --user maxmorlock --password secret ``` ### Options -This script can also be used to calculate reasonable, best-practice options for starting Java apps in general. For example, when running Maven in a container it makes sense to respect container Memory constraints. - The subcommand `options` can be used to print options to standard output so that is can be easily used to feed it to another, Java based application. When no extra arguments are given, all defaults will be used, which can be influenced with the environment variables described above. @@ -160,19 +147,15 @@ When no extra arguments are given, all defaults will be used, which can be influ You can select specific sets of options by providing additional arguments: * `--debug` : Java debug options if `JAVA_DEBUG` is set -* `--memory` : Memory settings based on the environment variables given * `--proxy` : Evaluate proxy environments variables -* `--cpu` : Tuning when the number of cores is limited -* `--gc` : GC tuning parameters -* `--jit` : JIT options * `--diagnostics` : Print diagnostics options when `JAVA_DIAGNOSTICS` is set -* `--java-default` : Same as `--memory --jit --diagnostic --cpu --gc` +* `--java-default` : Defaults Example: ``` -# Call Maven with the proper memory settings when running in an container -export MAVEN_OPTS="$(run-java.sh options --memory)" +# Call Maven with the proper proxy settings when running in an container +export MAVEN_OPTS="$(run-java.sh options --proxy)" mvn clean install ``` diff --git a/images/centos/openjdk11/jre/run-java.sh b/images/centos/openjdk11/jre/run-java.sh index 1ebd872..23a973c 100644 --- a/images/centos/openjdk11/jre/run-java.sh +++ b/images/centos/openjdk11/jre/run-java.sh @@ -1,7 +1,8 @@ #!/bin/sh -# =================================================================================== -# Generic startup script for running arbitrary Java applications with -# being optimized for running in containers +# ============================================================================================================== +# Generic startup script for running arbitrary Java applications for JVM with support for -XX+UseContainerSupport +# and -XX:MaxRAMPercentage, -XX:MinRAMPercentage and -XX:InitialRAMPercentage +# for running in containers # # Usage: # # Execute a Java app: @@ -20,32 +21,16 @@ # Env-variables evaluated in this script: # # JAVA_OPTIONS: Checked for already set options -# JAVA_MAX_MEM_RATIO: Ratio use to calculate a default maximum Memory, in percent. -# E.g. the "50" value implies that 50% of the Memory -# given to the container is used as the maximum heap memory with -# '-Xmx'. -# It defaults to "25" when the maximum amount of memory available -# to the container is below 300M, otherwise defaults to "50". -# It is a heuristic and should be better backed up with real -# experiments and measurements. -# For a good overviews what tuning options are available --> -# https://youtu.be/Vt4G-pHXfs4 -# https://www.youtube.com/watch?v=w1rZOY5gbvk -# https://vimeo.com/album/4133413/video/181900266 -# Also note that heap is only a small portion of the memory used by a JVM. There are lot -# of other memory areas (metadata, thread, code cache, ...) which addes to the overall -# size. When your container gets killed because of an OOM, then you should tune -# the absolute values. -# JAVA_INIT_MEM_RATIO: Ratio use to calculate a default intial heap memory, in percent. -# By default this value is not set. -# -# The following variables are exposed to your Java application: -# -# CONTAINER_MAX_MEMORY: Max memory for the container (if running within a container) -# MAX_CORE_LIMIT: Number of cores available for the container (if running within a container) +# JVM values to set for heap size: +# -XX:MaxRAMPercentage +# -XX:MinRAMPercentage +# -XX:InitialRAMPercentage +# By default the values of Min and Max RAMPerdentage is set to 85.0 +# to allow the container's 85% of memory to be used for the max heap. +# Initial RAM Percentage is not set. -# ========================================================== +# ================================================================================================================= # Fail on a single failed command in a pipeline (if supported) (set -o | grep -q pipefail) && set -o pipefail @@ -122,71 +107,6 @@ find_jar_file() { fi } -# Generic formula evaluation based on awk -calc() { - local formula="$1" - shift - echo "$@" | awk ' - function ceil(x) { - return x % 1 ? int(x) + 1 : x - } - function log2(x) { - return log(x)/log(2) - } - function max2(x, y) { - return x > y ? x : y - } - function round(x) { - return int(x + 0.5) - } - {print '"int(${formula})"'} - ' -} - -# Based on the cgroup limits, figure out the max number of core we should utilize -core_limit() { - local cpu_period_file="/sys/fs/cgroup/cpu/cpu.cfs_period_us" - local cpu_quota_file="/sys/fs/cgroup/cpu/cpu.cfs_quota_us" - if [ -r "${cpu_period_file}" ]; then - local cpu_period="$(cat ${cpu_period_file})" - - if [ -r "${cpu_quota_file}" ]; then - local cpu_quota="$(cat ${cpu_quota_file})" - # cfs_quota_us == -1 --> no restrictions - if [ ${cpu_quota:-0} -ne -1 ]; then - echo $(calc 'ceil($1/$2)' "${cpu_quota}" "${cpu_period}") - fi - fi - fi -} - -max_memory() { - # High number which is the max limit until which memory is supposed to be - # unbounded. - local mem_file="/sys/fs/cgroup/memory/memory.limit_in_bytes" - if [ -r "${mem_file}" ]; then - local max_mem_cgroup="$(cat ${mem_file})" - local max_mem_meminfo_kb="$(cat /proc/meminfo | awk '/MemTotal/ {print $2}')" - local max_mem_meminfo="$(expr $max_mem_meminfo_kb \* 1024)" - if [ ${max_mem_cgroup:-0} != -1 ] && [ ${max_mem_cgroup:-0} -lt ${max_mem_meminfo:-0} ] - then - echo "${max_mem_cgroup}" - fi - fi -} - -init_limit_env_vars() { - # Read in container limits and export the as environment variables - local core_limit="$(core_limit)" - if [ -n "${core_limit}" ]; then - export CONTAINER_CORE_LIMIT="${core_limit}" - fi - - local mem_limit="$(max_memory)" - if [ -n "${mem_limit}" ]; then - export CONTAINER_MAX_MEMORY="${mem_limit}" - fi -} load_env() { local script_dir="$1" @@ -284,88 +204,31 @@ format_classpath() { # ========================================================================== memory_options() { - echo "$(calc_init_memory) $(calc_max_memory)" + echo "$(set_max_memory)" return } -# Check for memory options and set max heap size if needed -calc_max_memory() { +# Check for memory options and set max heap size as percentage of RAM, if needed +set_max_memory() { + local MaxRAMPercentage="" + local MinRAMPercentage="" + # Check whether -Xmx is already given in JAVA_OPTIONS if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xmx"; then return fi - - if [ -z "${CONTAINER_MAX_MEMORY:-}" ]; then - return - fi - - # Check for the 'real memory size' and calculate Xmx from the ratio - if [ -n "${JAVA_MAX_MEM_RATIO:-}" ]; then - if [ "${JAVA_MAX_MEM_RATIO}" -eq 0 ]; then - # Explicitely switched off - return - fi - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_MAX_MEM_RATIO}" "mx" - # When JAVA_MAX_MEM_RATIO not set and JVM >= 10 no max_memory - elif [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - elif [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - # Restore the one-fourth default heap size instead of the one-half below 300MB threshold - # See https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "25" "mx" - else - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "50" "mx" - fi -} - -# Check for memory options and set initial heap size if requested -calc_init_memory() { - # Check whether -Xms is already given in JAVA_OPTIONS. - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xms"; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MaxRAMPercentage"); then + MaxRAMPercentage="-XX:MaxRAMPercentage=85.0" fi - - # Check if value set - if [ -z "${JAVA_INIT_MEM_RATIO:-}" ] || [ -z "${CONTAINER_MAX_MEMORY:-}" ] || [ "${JAVA_INIT_MEM_RATIO}" -eq 0 ]; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MinRAMPercentage"); then + MinRAMPercentage="-XX:MinRAMPercentage=85.0" fi - # Calculate Xms from the ratio given - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_INIT_MEM_RATIO}" "ms" + echo "${MaxRAMPercentage} ${MinRAMPercentage}" } -calc_mem_opt() { - local max_mem="$1" - local fraction="$2" - local mem_opt="$3" - - local val=$(calc 'round($1*$2/100/1048576)' "${max_mem}" "${fraction}") - echo "-X${mem_opt}${val}m" -} - -c2_disabled() { - if [ -n "${CONTAINER_MAX_MEMORY:-}" ]; then - # Disable C2 compiler when container memory <=300MB - if [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - echo true - return - fi - fi - echo false -} - -jit_options() { - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - # Check whether -XX:TieredStopAtLevel is already given in JAVA_OPTIONS - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:TieredStopAtLevel"; then - return - fi - if [ $(c2_disabled) = true ]; then - echo "-XX:TieredStopAtLevel=1" - fi -} # Switch on diagnostics except when switched off diagnostics_options() { @@ -378,72 +241,9 @@ diagnostics_options() { fi } -# Replicate thread ergonomics for tiered compilation. -# This could ideally be skipped when tiered compilation is disabled. -# The algorithm is taken from: -# OpenJDK / jdk8u / jdk8u / hotspot -# src/share/vm/runtime/advancedThresholdPolicy.cpp -ci_compiler_count() { - local core_limit="$1" - local log_cpu=$(calc 'log2($1)' "$core_limit") - local loglog_cpu=$(calc 'log2(max2($1,1))' "$log_cpu") - local count=$(calc 'max2($1*$2,1)*3/2' "$log_cpu" "$loglog_cpu") - local c1_count=$(calc 'max2($1/3,1)' "$count") - local c2_count=$(calc 'max2($1-$2,1)' "$count" "$c1_count") - [ $(c2_disabled) = true ] && echo "$c1_count" || echo $(calc '$1+$2' "$c1_count" "$c2_count") -} - -cpu_options() { - # JVMs >= 10 know about CPU limits - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - - local core_limit="${JAVA_CORE_LIMIT:-}" - if [ "$core_limit" = "0" ]; then - return - fi - - if [ -n "${CONTAINER_CORE_LIMIT:-}" ]; then - if [ -z ${core_limit} ]; then - core_limit="${CONTAINER_CORE_LIMIT}" - fi - echo "-XX:ParallelGCThreads=${core_limit} " \ - "-XX:ConcGCThreads=${core_limit} " \ - "-Djava.util.concurrent.ForkJoinPool.common.parallelism=${core_limit} " \ - "-XX:CICompilerCount=$(ci_compiler_count $core_limit)" - fi -} - -#-XX:MinHeapFreeRatio=20 These parameters tell the heap to shrink aggressively and to grow conservatively. -#-XX:MaxHeapFreeRatio=40 Thereby optimizing the amount of memory available to the operating system. -heap_ratio() { - echo "-XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40" -} - -# These parameters are necessary when running parallel GC if you want to use the Min and Max Heap Free ratios. -# Skip setting gc_options if any other GC is set in JAVA_OPTIONS. -# -XX:GCTimeRatio=4 -# -XX:AdaptiveSizePolicyWeight=90 -gc_options() { - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:.*Use.*GC"; then - return - fi - - local opts="" - # for JVMs < 10 set GC settings - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-0}" -lt "10" ]; then - opts="${opts} -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 $(heap_ratio)" - fi - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-}" != "7" ]; then - opts="${opts} -XX:+ExitOnOutOfMemoryError" - fi - echo $opts -} - java_default_options() { # Echo options, trimming trailing and multiple spaces - echo "$(memory_options) $(jit_options) $(diagnostics_options) $(cpu_options) $(gc_options)" | awk '$1=$1' + echo "$(memory_options) $(diagnostics_options)" | awk '$1=$1' } @@ -564,21 +364,9 @@ options() { if [ $(hasflag --java-default) ]; then ret="$ret $(java_default_options)" fi - if [ $(hasflag --memory) ]; then - ret="$ret $(memory_options)" - fi - if [ $(hasflag --jit) ]; then - ret="$ret $(jit_options)" - fi if [ $(hasflag --diagnostics) ]; then ret="$ret $(diagnostics_options)" fi - if [ $(hasflag --cpu) ]; then - ret="$ret $(cpu_options)" - fi - if [ $(hasflag --gc) ]; then - ret="$ret $(gc_options)" - fi echo $ret | awk '$1=$1' } @@ -608,9 +396,6 @@ run() { # ============================================================================= # Fire up -# Set env vars reflecting limits -init_limit_env_vars - first_arg=${1:-} if [ "${first_arg}" = "options" ]; then # Print out options only @@ -622,4 +407,4 @@ elif [ "${first_arg}" = "run" ]; then # as first argument to your shift fi -run "$@" +run "$@" \ No newline at end of file diff --git a/images/centos/openjdk8/jdk/README.md b/images/centos/openjdk8/jdk/README.md index 02baf1c..b5afd59 100644 --- a/images/centos/openjdk8/jdk/README.md +++ b/images/centos/openjdk8/jdk/README.md @@ -96,10 +96,6 @@ The startup process is configured mostly via environment variables: * **JAVA_APP_DIR** the directory where the application resides. All paths in your application are relative to this directory. By default it is the same directory where this startup script resides. * **JAVA_LIB_DIR** directory holding the Java jar files as well an optional `classpath` file which holds the classpath. Either as a single line classpath (colon separated) or with jar files listed line-by-line. If not set **JAVA_LIB_DIR** is the same as **JAVA_APP_DIR**. * **JAVA_OPTIONS** options to add when calling `java` -* **JAVA_MAJOR_VERSION** a number >= 7. If the version is set then only options suitable for this version are used. When set to 7 options known only to Java > 8 will be removed. For versions >= 10 no explicit memory limit is calculated since Java >= 10 has support for container limits. -* **JAVA_MAX_MEM_RATIO** is used when no `-Xmx` option is given in `JAVA_OPTIONS`. This is used to calculate a default maximal Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio of the container available memory as set here. The default is `25` when the maximum amount of memory available to the container is below 300M, `50` otherwise, which means in that case that 50% of the available memory is used as an upper boundary. You can skip this mechanism by setting this value to 0 in which case no `-Xmx` option is added. -* **JAVA_INIT_MEM_RATIO** is used when no `-Xms` option is given in `JAVA_OPTIONS`. This is used to calculate a default initial Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xms` is set to a ratio of the container available memory as set here. By default this value is not set. -* **JAVA_MAX_CORE** restrict manually the number of cores available which is used for calculating certain defaults like the number of garbage collector threads. If set to 0 no base JVM tuning based on the number of cores is performed. * **JAVA_DIAGNOSTICS** set this to get some diagnostics information to standard out when things are happening * **JAVA_MAIN_CLASS** A main class to use as argument for `java`. When this environment variable is given, all jar files in `$JAVA_APP_DIR` are added to the classpath as well as `$JAVA_LIB_DIR`. * **JAVA_APP_JAR** A jar file with an appropriate manifest so that it can be started with `java -jar` if no `$JAVA_MAIN_CLASS` is set. In all cases this jar file is added to the classpath, too. @@ -131,11 +127,6 @@ These variables can be also set in a shell config file `run-env.sh`, which will This startup script also checks for a command `run-java-options`. If existent it will be called and the output is added to the environment variable `$JAVA_OPTIONS`. -The startup script also exposes some environment variables describing container limits which can be used by applications: - -* **CONTAINER_CORE_LIMIT** a calculated core limit as described in https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt -* **CONTAINER_MAX_MEMORY** memory limit given to the container - Any arguments given to the script are given through directly as argument to the Java application. Example: @@ -143,16 +134,12 @@ Example: ``` # Set the application directory directly export JAVA_APP_DIR=/deployments -# Set -Xmx based on container constraints -export JAVA_MAX_MEM_RATIO=40 # Start the jar in JAVA_APP_DIR with the given arguments ./run-java.sh --user maxmorlock --password secret ``` ### Options -This script can also be used to calculate reasonable, best-practice options for starting Java apps in general. For example, when running Maven in a container it makes sense to respect container Memory constraints. - The subcommand `options` can be used to print options to standard output so that is can be easily used to feed it to another, Java based application. When no extra arguments are given, all defaults will be used, which can be influenced with the environment variables described above. @@ -160,19 +147,15 @@ When no extra arguments are given, all defaults will be used, which can be influ You can select specific sets of options by providing additional arguments: * `--debug` : Java debug options if `JAVA_DEBUG` is set -* `--memory` : Memory settings based on the environment variables given * `--proxy` : Evaluate proxy environments variables -* `--cpu` : Tuning when the number of cores is limited -* `--gc` : GC tuning parameters -* `--jit` : JIT options * `--diagnostics` : Print diagnostics options when `JAVA_DIAGNOSTICS` is set -* `--java-default` : Same as `--memory --jit --diagnostic --cpu --gc` +* `--java-default` : Defaults Example: ``` -# Call Maven with the proper memory settings when running in an container -export MAVEN_OPTS="$(run-java.sh options --memory)" +# Call Maven with the proper proxy settings when running in an container +export MAVEN_OPTS="$(run-java.sh options --proxy)" mvn clean install ``` diff --git a/images/centos/openjdk8/jdk/run-java.sh b/images/centos/openjdk8/jdk/run-java.sh index 1ebd872..23a973c 100644 --- a/images/centos/openjdk8/jdk/run-java.sh +++ b/images/centos/openjdk8/jdk/run-java.sh @@ -1,7 +1,8 @@ #!/bin/sh -# =================================================================================== -# Generic startup script for running arbitrary Java applications with -# being optimized for running in containers +# ============================================================================================================== +# Generic startup script for running arbitrary Java applications for JVM with support for -XX+UseContainerSupport +# and -XX:MaxRAMPercentage, -XX:MinRAMPercentage and -XX:InitialRAMPercentage +# for running in containers # # Usage: # # Execute a Java app: @@ -20,32 +21,16 @@ # Env-variables evaluated in this script: # # JAVA_OPTIONS: Checked for already set options -# JAVA_MAX_MEM_RATIO: Ratio use to calculate a default maximum Memory, in percent. -# E.g. the "50" value implies that 50% of the Memory -# given to the container is used as the maximum heap memory with -# '-Xmx'. -# It defaults to "25" when the maximum amount of memory available -# to the container is below 300M, otherwise defaults to "50". -# It is a heuristic and should be better backed up with real -# experiments and measurements. -# For a good overviews what tuning options are available --> -# https://youtu.be/Vt4G-pHXfs4 -# https://www.youtube.com/watch?v=w1rZOY5gbvk -# https://vimeo.com/album/4133413/video/181900266 -# Also note that heap is only a small portion of the memory used by a JVM. There are lot -# of other memory areas (metadata, thread, code cache, ...) which addes to the overall -# size. When your container gets killed because of an OOM, then you should tune -# the absolute values. -# JAVA_INIT_MEM_RATIO: Ratio use to calculate a default intial heap memory, in percent. -# By default this value is not set. -# -# The following variables are exposed to your Java application: -# -# CONTAINER_MAX_MEMORY: Max memory for the container (if running within a container) -# MAX_CORE_LIMIT: Number of cores available for the container (if running within a container) +# JVM values to set for heap size: +# -XX:MaxRAMPercentage +# -XX:MinRAMPercentage +# -XX:InitialRAMPercentage +# By default the values of Min and Max RAMPerdentage is set to 85.0 +# to allow the container's 85% of memory to be used for the max heap. +# Initial RAM Percentage is not set. -# ========================================================== +# ================================================================================================================= # Fail on a single failed command in a pipeline (if supported) (set -o | grep -q pipefail) && set -o pipefail @@ -122,71 +107,6 @@ find_jar_file() { fi } -# Generic formula evaluation based on awk -calc() { - local formula="$1" - shift - echo "$@" | awk ' - function ceil(x) { - return x % 1 ? int(x) + 1 : x - } - function log2(x) { - return log(x)/log(2) - } - function max2(x, y) { - return x > y ? x : y - } - function round(x) { - return int(x + 0.5) - } - {print '"int(${formula})"'} - ' -} - -# Based on the cgroup limits, figure out the max number of core we should utilize -core_limit() { - local cpu_period_file="/sys/fs/cgroup/cpu/cpu.cfs_period_us" - local cpu_quota_file="/sys/fs/cgroup/cpu/cpu.cfs_quota_us" - if [ -r "${cpu_period_file}" ]; then - local cpu_period="$(cat ${cpu_period_file})" - - if [ -r "${cpu_quota_file}" ]; then - local cpu_quota="$(cat ${cpu_quota_file})" - # cfs_quota_us == -1 --> no restrictions - if [ ${cpu_quota:-0} -ne -1 ]; then - echo $(calc 'ceil($1/$2)' "${cpu_quota}" "${cpu_period}") - fi - fi - fi -} - -max_memory() { - # High number which is the max limit until which memory is supposed to be - # unbounded. - local mem_file="/sys/fs/cgroup/memory/memory.limit_in_bytes" - if [ -r "${mem_file}" ]; then - local max_mem_cgroup="$(cat ${mem_file})" - local max_mem_meminfo_kb="$(cat /proc/meminfo | awk '/MemTotal/ {print $2}')" - local max_mem_meminfo="$(expr $max_mem_meminfo_kb \* 1024)" - if [ ${max_mem_cgroup:-0} != -1 ] && [ ${max_mem_cgroup:-0} -lt ${max_mem_meminfo:-0} ] - then - echo "${max_mem_cgroup}" - fi - fi -} - -init_limit_env_vars() { - # Read in container limits and export the as environment variables - local core_limit="$(core_limit)" - if [ -n "${core_limit}" ]; then - export CONTAINER_CORE_LIMIT="${core_limit}" - fi - - local mem_limit="$(max_memory)" - if [ -n "${mem_limit}" ]; then - export CONTAINER_MAX_MEMORY="${mem_limit}" - fi -} load_env() { local script_dir="$1" @@ -284,88 +204,31 @@ format_classpath() { # ========================================================================== memory_options() { - echo "$(calc_init_memory) $(calc_max_memory)" + echo "$(set_max_memory)" return } -# Check for memory options and set max heap size if needed -calc_max_memory() { +# Check for memory options and set max heap size as percentage of RAM, if needed +set_max_memory() { + local MaxRAMPercentage="" + local MinRAMPercentage="" + # Check whether -Xmx is already given in JAVA_OPTIONS if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xmx"; then return fi - - if [ -z "${CONTAINER_MAX_MEMORY:-}" ]; then - return - fi - - # Check for the 'real memory size' and calculate Xmx from the ratio - if [ -n "${JAVA_MAX_MEM_RATIO:-}" ]; then - if [ "${JAVA_MAX_MEM_RATIO}" -eq 0 ]; then - # Explicitely switched off - return - fi - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_MAX_MEM_RATIO}" "mx" - # When JAVA_MAX_MEM_RATIO not set and JVM >= 10 no max_memory - elif [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - elif [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - # Restore the one-fourth default heap size instead of the one-half below 300MB threshold - # See https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "25" "mx" - else - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "50" "mx" - fi -} - -# Check for memory options and set initial heap size if requested -calc_init_memory() { - # Check whether -Xms is already given in JAVA_OPTIONS. - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xms"; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MaxRAMPercentage"); then + MaxRAMPercentage="-XX:MaxRAMPercentage=85.0" fi - - # Check if value set - if [ -z "${JAVA_INIT_MEM_RATIO:-}" ] || [ -z "${CONTAINER_MAX_MEMORY:-}" ] || [ "${JAVA_INIT_MEM_RATIO}" -eq 0 ]; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MinRAMPercentage"); then + MinRAMPercentage="-XX:MinRAMPercentage=85.0" fi - # Calculate Xms from the ratio given - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_INIT_MEM_RATIO}" "ms" + echo "${MaxRAMPercentage} ${MinRAMPercentage}" } -calc_mem_opt() { - local max_mem="$1" - local fraction="$2" - local mem_opt="$3" - - local val=$(calc 'round($1*$2/100/1048576)' "${max_mem}" "${fraction}") - echo "-X${mem_opt}${val}m" -} - -c2_disabled() { - if [ -n "${CONTAINER_MAX_MEMORY:-}" ]; then - # Disable C2 compiler when container memory <=300MB - if [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - echo true - return - fi - fi - echo false -} - -jit_options() { - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - # Check whether -XX:TieredStopAtLevel is already given in JAVA_OPTIONS - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:TieredStopAtLevel"; then - return - fi - if [ $(c2_disabled) = true ]; then - echo "-XX:TieredStopAtLevel=1" - fi -} # Switch on diagnostics except when switched off diagnostics_options() { @@ -378,72 +241,9 @@ diagnostics_options() { fi } -# Replicate thread ergonomics for tiered compilation. -# This could ideally be skipped when tiered compilation is disabled. -# The algorithm is taken from: -# OpenJDK / jdk8u / jdk8u / hotspot -# src/share/vm/runtime/advancedThresholdPolicy.cpp -ci_compiler_count() { - local core_limit="$1" - local log_cpu=$(calc 'log2($1)' "$core_limit") - local loglog_cpu=$(calc 'log2(max2($1,1))' "$log_cpu") - local count=$(calc 'max2($1*$2,1)*3/2' "$log_cpu" "$loglog_cpu") - local c1_count=$(calc 'max2($1/3,1)' "$count") - local c2_count=$(calc 'max2($1-$2,1)' "$count" "$c1_count") - [ $(c2_disabled) = true ] && echo "$c1_count" || echo $(calc '$1+$2' "$c1_count" "$c2_count") -} - -cpu_options() { - # JVMs >= 10 know about CPU limits - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - - local core_limit="${JAVA_CORE_LIMIT:-}" - if [ "$core_limit" = "0" ]; then - return - fi - - if [ -n "${CONTAINER_CORE_LIMIT:-}" ]; then - if [ -z ${core_limit} ]; then - core_limit="${CONTAINER_CORE_LIMIT}" - fi - echo "-XX:ParallelGCThreads=${core_limit} " \ - "-XX:ConcGCThreads=${core_limit} " \ - "-Djava.util.concurrent.ForkJoinPool.common.parallelism=${core_limit} " \ - "-XX:CICompilerCount=$(ci_compiler_count $core_limit)" - fi -} - -#-XX:MinHeapFreeRatio=20 These parameters tell the heap to shrink aggressively and to grow conservatively. -#-XX:MaxHeapFreeRatio=40 Thereby optimizing the amount of memory available to the operating system. -heap_ratio() { - echo "-XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40" -} - -# These parameters are necessary when running parallel GC if you want to use the Min and Max Heap Free ratios. -# Skip setting gc_options if any other GC is set in JAVA_OPTIONS. -# -XX:GCTimeRatio=4 -# -XX:AdaptiveSizePolicyWeight=90 -gc_options() { - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:.*Use.*GC"; then - return - fi - - local opts="" - # for JVMs < 10 set GC settings - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-0}" -lt "10" ]; then - opts="${opts} -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 $(heap_ratio)" - fi - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-}" != "7" ]; then - opts="${opts} -XX:+ExitOnOutOfMemoryError" - fi - echo $opts -} - java_default_options() { # Echo options, trimming trailing and multiple spaces - echo "$(memory_options) $(jit_options) $(diagnostics_options) $(cpu_options) $(gc_options)" | awk '$1=$1' + echo "$(memory_options) $(diagnostics_options)" | awk '$1=$1' } @@ -564,21 +364,9 @@ options() { if [ $(hasflag --java-default) ]; then ret="$ret $(java_default_options)" fi - if [ $(hasflag --memory) ]; then - ret="$ret $(memory_options)" - fi - if [ $(hasflag --jit) ]; then - ret="$ret $(jit_options)" - fi if [ $(hasflag --diagnostics) ]; then ret="$ret $(diagnostics_options)" fi - if [ $(hasflag --cpu) ]; then - ret="$ret $(cpu_options)" - fi - if [ $(hasflag --gc) ]; then - ret="$ret $(gc_options)" - fi echo $ret | awk '$1=$1' } @@ -608,9 +396,6 @@ run() { # ============================================================================= # Fire up -# Set env vars reflecting limits -init_limit_env_vars - first_arg=${1:-} if [ "${first_arg}" = "options" ]; then # Print out options only @@ -622,4 +407,4 @@ elif [ "${first_arg}" = "run" ]; then # as first argument to your shift fi -run "$@" +run "$@" \ No newline at end of file diff --git a/images/centos/openjdk8/jre/README.md b/images/centos/openjdk8/jre/README.md index d7479c5..17440f1 100644 --- a/images/centos/openjdk8/jre/README.md +++ b/images/centos/openjdk8/jre/README.md @@ -96,10 +96,6 @@ The startup process is configured mostly via environment variables: * **JAVA_APP_DIR** the directory where the application resides. All paths in your application are relative to this directory. By default it is the same directory where this startup script resides. * **JAVA_LIB_DIR** directory holding the Java jar files as well an optional `classpath` file which holds the classpath. Either as a single line classpath (colon separated) or with jar files listed line-by-line. If not set **JAVA_LIB_DIR** is the same as **JAVA_APP_DIR**. * **JAVA_OPTIONS** options to add when calling `java` -* **JAVA_MAJOR_VERSION** a number >= 7. If the version is set then only options suitable for this version are used. When set to 7 options known only to Java > 8 will be removed. For versions >= 10 no explicit memory limit is calculated since Java >= 10 has support for container limits. -* **JAVA_MAX_MEM_RATIO** is used when no `-Xmx` option is given in `JAVA_OPTIONS`. This is used to calculate a default maximal Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio of the container available memory as set here. The default is `25` when the maximum amount of memory available to the container is below 300M, `50` otherwise, which means in that case that 50% of the available memory is used as an upper boundary. You can skip this mechanism by setting this value to 0 in which case no `-Xmx` option is added. -* **JAVA_INIT_MEM_RATIO** is used when no `-Xms` option is given in `JAVA_OPTIONS`. This is used to calculate a default initial Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xms` is set to a ratio of the container available memory as set here. By default this value is not set. -* **JAVA_MAX_CORE** restrict manually the number of cores available which is used for calculating certain defaults like the number of garbage collector threads. If set to 0 no base JVM tuning based on the number of cores is performed. * **JAVA_DIAGNOSTICS** set this to get some diagnostics information to standard out when things are happening * **JAVA_MAIN_CLASS** A main class to use as argument for `java`. When this environment variable is given, all jar files in `$JAVA_APP_DIR` are added to the classpath as well as `$JAVA_LIB_DIR`. * **JAVA_APP_JAR** A jar file with an appropriate manifest so that it can be started with `java -jar` if no `$JAVA_MAIN_CLASS` is set. In all cases this jar file is added to the classpath, too. @@ -131,11 +127,6 @@ These variables can be also set in a shell config file `run-env.sh`, which will This startup script also checks for a command `run-java-options`. If existent it will be called and the output is added to the environment variable `$JAVA_OPTIONS`. -The startup script also exposes some environment variables describing container limits which can be used by applications: - -* **CONTAINER_CORE_LIMIT** a calculated core limit as described in https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt -* **CONTAINER_MAX_MEMORY** memory limit given to the container - Any arguments given to the script are given through directly as argument to the Java application. Example: @@ -143,16 +134,12 @@ Example: ``` # Set the application directory directly export JAVA_APP_DIR=/deployments -# Set -Xmx based on container constraints -export JAVA_MAX_MEM_RATIO=40 # Start the jar in JAVA_APP_DIR with the given arguments ./run-java.sh --user maxmorlock --password secret ``` ### Options -This script can also be used to calculate reasonable, best-practice options for starting Java apps in general. For example, when running Maven in a container it makes sense to respect container Memory constraints. - The subcommand `options` can be used to print options to standard output so that is can be easily used to feed it to another, Java based application. When no extra arguments are given, all defaults will be used, which can be influenced with the environment variables described above. @@ -160,19 +147,15 @@ When no extra arguments are given, all defaults will be used, which can be influ You can select specific sets of options by providing additional arguments: * `--debug` : Java debug options if `JAVA_DEBUG` is set -* `--memory` : Memory settings based on the environment variables given * `--proxy` : Evaluate proxy environments variables -* `--cpu` : Tuning when the number of cores is limited -* `--gc` : GC tuning parameters -* `--jit` : JIT options * `--diagnostics` : Print diagnostics options when `JAVA_DIAGNOSTICS` is set -* `--java-default` : Same as `--memory --jit --diagnostic --cpu --gc` +* `--java-default` : Default Example: ``` -# Call Maven with the proper memory settings when running in an container -export MAVEN_OPTS="$(run-java.sh options --memory)" +# Call Maven with the proper proxy settings when running in an container +export MAVEN_OPTS="$(run-java.sh options --proxy)" mvn clean install ``` diff --git a/images/centos/openjdk8/jre/run-java.sh b/images/centos/openjdk8/jre/run-java.sh index 1ebd872..23a973c 100644 --- a/images/centos/openjdk8/jre/run-java.sh +++ b/images/centos/openjdk8/jre/run-java.sh @@ -1,7 +1,8 @@ #!/bin/sh -# =================================================================================== -# Generic startup script for running arbitrary Java applications with -# being optimized for running in containers +# ============================================================================================================== +# Generic startup script for running arbitrary Java applications for JVM with support for -XX+UseContainerSupport +# and -XX:MaxRAMPercentage, -XX:MinRAMPercentage and -XX:InitialRAMPercentage +# for running in containers # # Usage: # # Execute a Java app: @@ -20,32 +21,16 @@ # Env-variables evaluated in this script: # # JAVA_OPTIONS: Checked for already set options -# JAVA_MAX_MEM_RATIO: Ratio use to calculate a default maximum Memory, in percent. -# E.g. the "50" value implies that 50% of the Memory -# given to the container is used as the maximum heap memory with -# '-Xmx'. -# It defaults to "25" when the maximum amount of memory available -# to the container is below 300M, otherwise defaults to "50". -# It is a heuristic and should be better backed up with real -# experiments and measurements. -# For a good overviews what tuning options are available --> -# https://youtu.be/Vt4G-pHXfs4 -# https://www.youtube.com/watch?v=w1rZOY5gbvk -# https://vimeo.com/album/4133413/video/181900266 -# Also note that heap is only a small portion of the memory used by a JVM. There are lot -# of other memory areas (metadata, thread, code cache, ...) which addes to the overall -# size. When your container gets killed because of an OOM, then you should tune -# the absolute values. -# JAVA_INIT_MEM_RATIO: Ratio use to calculate a default intial heap memory, in percent. -# By default this value is not set. -# -# The following variables are exposed to your Java application: -# -# CONTAINER_MAX_MEMORY: Max memory for the container (if running within a container) -# MAX_CORE_LIMIT: Number of cores available for the container (if running within a container) +# JVM values to set for heap size: +# -XX:MaxRAMPercentage +# -XX:MinRAMPercentage +# -XX:InitialRAMPercentage +# By default the values of Min and Max RAMPerdentage is set to 85.0 +# to allow the container's 85% of memory to be used for the max heap. +# Initial RAM Percentage is not set. -# ========================================================== +# ================================================================================================================= # Fail on a single failed command in a pipeline (if supported) (set -o | grep -q pipefail) && set -o pipefail @@ -122,71 +107,6 @@ find_jar_file() { fi } -# Generic formula evaluation based on awk -calc() { - local formula="$1" - shift - echo "$@" | awk ' - function ceil(x) { - return x % 1 ? int(x) + 1 : x - } - function log2(x) { - return log(x)/log(2) - } - function max2(x, y) { - return x > y ? x : y - } - function round(x) { - return int(x + 0.5) - } - {print '"int(${formula})"'} - ' -} - -# Based on the cgroup limits, figure out the max number of core we should utilize -core_limit() { - local cpu_period_file="/sys/fs/cgroup/cpu/cpu.cfs_period_us" - local cpu_quota_file="/sys/fs/cgroup/cpu/cpu.cfs_quota_us" - if [ -r "${cpu_period_file}" ]; then - local cpu_period="$(cat ${cpu_period_file})" - - if [ -r "${cpu_quota_file}" ]; then - local cpu_quota="$(cat ${cpu_quota_file})" - # cfs_quota_us == -1 --> no restrictions - if [ ${cpu_quota:-0} -ne -1 ]; then - echo $(calc 'ceil($1/$2)' "${cpu_quota}" "${cpu_period}") - fi - fi - fi -} - -max_memory() { - # High number which is the max limit until which memory is supposed to be - # unbounded. - local mem_file="/sys/fs/cgroup/memory/memory.limit_in_bytes" - if [ -r "${mem_file}" ]; then - local max_mem_cgroup="$(cat ${mem_file})" - local max_mem_meminfo_kb="$(cat /proc/meminfo | awk '/MemTotal/ {print $2}')" - local max_mem_meminfo="$(expr $max_mem_meminfo_kb \* 1024)" - if [ ${max_mem_cgroup:-0} != -1 ] && [ ${max_mem_cgroup:-0} -lt ${max_mem_meminfo:-0} ] - then - echo "${max_mem_cgroup}" - fi - fi -} - -init_limit_env_vars() { - # Read in container limits and export the as environment variables - local core_limit="$(core_limit)" - if [ -n "${core_limit}" ]; then - export CONTAINER_CORE_LIMIT="${core_limit}" - fi - - local mem_limit="$(max_memory)" - if [ -n "${mem_limit}" ]; then - export CONTAINER_MAX_MEMORY="${mem_limit}" - fi -} load_env() { local script_dir="$1" @@ -284,88 +204,31 @@ format_classpath() { # ========================================================================== memory_options() { - echo "$(calc_init_memory) $(calc_max_memory)" + echo "$(set_max_memory)" return } -# Check for memory options and set max heap size if needed -calc_max_memory() { +# Check for memory options and set max heap size as percentage of RAM, if needed +set_max_memory() { + local MaxRAMPercentage="" + local MinRAMPercentage="" + # Check whether -Xmx is already given in JAVA_OPTIONS if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xmx"; then return fi - - if [ -z "${CONTAINER_MAX_MEMORY:-}" ]; then - return - fi - - # Check for the 'real memory size' and calculate Xmx from the ratio - if [ -n "${JAVA_MAX_MEM_RATIO:-}" ]; then - if [ "${JAVA_MAX_MEM_RATIO}" -eq 0 ]; then - # Explicitely switched off - return - fi - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_MAX_MEM_RATIO}" "mx" - # When JAVA_MAX_MEM_RATIO not set and JVM >= 10 no max_memory - elif [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - elif [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - # Restore the one-fourth default heap size instead of the one-half below 300MB threshold - # See https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "25" "mx" - else - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "50" "mx" - fi -} - -# Check for memory options and set initial heap size if requested -calc_init_memory() { - # Check whether -Xms is already given in JAVA_OPTIONS. - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xms"; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MaxRAMPercentage"); then + MaxRAMPercentage="-XX:MaxRAMPercentage=85.0" fi - - # Check if value set - if [ -z "${JAVA_INIT_MEM_RATIO:-}" ] || [ -z "${CONTAINER_MAX_MEMORY:-}" ] || [ "${JAVA_INIT_MEM_RATIO}" -eq 0 ]; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MinRAMPercentage"); then + MinRAMPercentage="-XX:MinRAMPercentage=85.0" fi - # Calculate Xms from the ratio given - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_INIT_MEM_RATIO}" "ms" + echo "${MaxRAMPercentage} ${MinRAMPercentage}" } -calc_mem_opt() { - local max_mem="$1" - local fraction="$2" - local mem_opt="$3" - - local val=$(calc 'round($1*$2/100/1048576)' "${max_mem}" "${fraction}") - echo "-X${mem_opt}${val}m" -} - -c2_disabled() { - if [ -n "${CONTAINER_MAX_MEMORY:-}" ]; then - # Disable C2 compiler when container memory <=300MB - if [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - echo true - return - fi - fi - echo false -} - -jit_options() { - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - # Check whether -XX:TieredStopAtLevel is already given in JAVA_OPTIONS - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:TieredStopAtLevel"; then - return - fi - if [ $(c2_disabled) = true ]; then - echo "-XX:TieredStopAtLevel=1" - fi -} # Switch on diagnostics except when switched off diagnostics_options() { @@ -378,72 +241,9 @@ diagnostics_options() { fi } -# Replicate thread ergonomics for tiered compilation. -# This could ideally be skipped when tiered compilation is disabled. -# The algorithm is taken from: -# OpenJDK / jdk8u / jdk8u / hotspot -# src/share/vm/runtime/advancedThresholdPolicy.cpp -ci_compiler_count() { - local core_limit="$1" - local log_cpu=$(calc 'log2($1)' "$core_limit") - local loglog_cpu=$(calc 'log2(max2($1,1))' "$log_cpu") - local count=$(calc 'max2($1*$2,1)*3/2' "$log_cpu" "$loglog_cpu") - local c1_count=$(calc 'max2($1/3,1)' "$count") - local c2_count=$(calc 'max2($1-$2,1)' "$count" "$c1_count") - [ $(c2_disabled) = true ] && echo "$c1_count" || echo $(calc '$1+$2' "$c1_count" "$c2_count") -} - -cpu_options() { - # JVMs >= 10 know about CPU limits - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - - local core_limit="${JAVA_CORE_LIMIT:-}" - if [ "$core_limit" = "0" ]; then - return - fi - - if [ -n "${CONTAINER_CORE_LIMIT:-}" ]; then - if [ -z ${core_limit} ]; then - core_limit="${CONTAINER_CORE_LIMIT}" - fi - echo "-XX:ParallelGCThreads=${core_limit} " \ - "-XX:ConcGCThreads=${core_limit} " \ - "-Djava.util.concurrent.ForkJoinPool.common.parallelism=${core_limit} " \ - "-XX:CICompilerCount=$(ci_compiler_count $core_limit)" - fi -} - -#-XX:MinHeapFreeRatio=20 These parameters tell the heap to shrink aggressively and to grow conservatively. -#-XX:MaxHeapFreeRatio=40 Thereby optimizing the amount of memory available to the operating system. -heap_ratio() { - echo "-XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40" -} - -# These parameters are necessary when running parallel GC if you want to use the Min and Max Heap Free ratios. -# Skip setting gc_options if any other GC is set in JAVA_OPTIONS. -# -XX:GCTimeRatio=4 -# -XX:AdaptiveSizePolicyWeight=90 -gc_options() { - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:.*Use.*GC"; then - return - fi - - local opts="" - # for JVMs < 10 set GC settings - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-0}" -lt "10" ]; then - opts="${opts} -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 $(heap_ratio)" - fi - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-}" != "7" ]; then - opts="${opts} -XX:+ExitOnOutOfMemoryError" - fi - echo $opts -} - java_default_options() { # Echo options, trimming trailing and multiple spaces - echo "$(memory_options) $(jit_options) $(diagnostics_options) $(cpu_options) $(gc_options)" | awk '$1=$1' + echo "$(memory_options) $(diagnostics_options)" | awk '$1=$1' } @@ -564,21 +364,9 @@ options() { if [ $(hasflag --java-default) ]; then ret="$ret $(java_default_options)" fi - if [ $(hasflag --memory) ]; then - ret="$ret $(memory_options)" - fi - if [ $(hasflag --jit) ]; then - ret="$ret $(jit_options)" - fi if [ $(hasflag --diagnostics) ]; then ret="$ret $(diagnostics_options)" fi - if [ $(hasflag --cpu) ]; then - ret="$ret $(cpu_options)" - fi - if [ $(hasflag --gc) ]; then - ret="$ret $(gc_options)" - fi echo $ret | awk '$1=$1' } @@ -608,9 +396,6 @@ run() { # ============================================================================= # Fire up -# Set env vars reflecting limits -init_limit_env_vars - first_arg=${1:-} if [ "${first_arg}" = "options" ]; then # Print out options only @@ -622,4 +407,4 @@ elif [ "${first_arg}" = "run" ]; then # as first argument to your shift fi -run "$@" +run "$@" \ No newline at end of file diff --git a/images/jboss/openjdk8/jdk/README.md b/images/jboss/openjdk8/jdk/README.md index 4275521..f720655 100644 --- a/images/jboss/openjdk8/jdk/README.md +++ b/images/jboss/openjdk8/jdk/README.md @@ -99,10 +99,6 @@ The startup process is configured mostly via environment variables: * **JAVA_APP_DIR** the directory where the application resides. All paths in your application are relative to this directory. By default it is the same directory where this startup script resides. * **JAVA_LIB_DIR** directory holding the Java jar files as well an optional `classpath` file which holds the classpath. Either as a single line classpath (colon separated) or with jar files listed line-by-line. If not set **JAVA_LIB_DIR** is the same as **JAVA_APP_DIR**. * **JAVA_OPTIONS** options to add when calling `java` -* **JAVA_MAJOR_VERSION** a number >= 7. If the version is set then only options suitable for this version are used. When set to 7 options known only to Java > 8 will be removed. For versions >= 10 no explicit memory limit is calculated since Java >= 10 has support for container limits. -* **JAVA_MAX_MEM_RATIO** is used when no `-Xmx` option is given in `JAVA_OPTIONS`. This is used to calculate a default maximal Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio of the container available memory as set here. The default is `25` when the maximum amount of memory available to the container is below 300M, `50` otherwise, which means in that case that 50% of the available memory is used as an upper boundary. You can skip this mechanism by setting this value to 0 in which case no `-Xmx` option is added. -* **JAVA_INIT_MEM_RATIO** is used when no `-Xms` option is given in `JAVA_OPTIONS`. This is used to calculate a default initial Heap Memory based on a containers restriction. If used in a Docker container without any memory constraints for the container then this option has no effect. If there is a memory constraint then `-Xms` is set to a ratio of the container available memory as set here. By default this value is not set. -* **JAVA_MAX_CORE** restrict manually the number of cores available which is used for calculating certain defaults like the number of garbage collector threads. If set to 0 no base JVM tuning based on the number of cores is performed. * **JAVA_DIAGNOSTICS** set this to get some diagnostics information to standard out when things are happening * **JAVA_MAIN_CLASS** A main class to use as argument for `java`. When this environment variable is given, all jar files in `$JAVA_APP_DIR` are added to the classpath as well as `$JAVA_LIB_DIR`. * **JAVA_APP_JAR** A jar file with an appropriate manifest so that it can be started with `java -jar` if no `$JAVA_MAIN_CLASS` is set. In all cases this jar file is added to the classpath, too. @@ -134,10 +130,6 @@ These variables can be also set in a shell config file `run-env.sh`, which will This startup script also checks for a command `run-java-options`. If existent it will be called and the output is added to the environment variable `$JAVA_OPTIONS`. -The startup script also exposes some environment variables describing container limits which can be used by applications: - -* **CONTAINER_CORE_LIMIT** a calculated core limit as described in https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt -* **CONTAINER_MAX_MEMORY** memory limit given to the container Any arguments given to the script are given through directly as argument to the Java application. @@ -146,16 +138,12 @@ Example: ``` # Set the application directory directly export JAVA_APP_DIR=/deployments -# Set -Xmx based on container constraints -export JAVA_MAX_MEM_RATIO=40 # Start the jar in JAVA_APP_DIR with the given arguments ./run-java.sh --user maxmorlock --password secret ``` ### Options -This script can also be used to calculate reasonable, best-practice options for starting Java apps in general. For example, when running Maven in a container it makes sense to respect container Memory constraints. - The subcommand `options` can be used to print options to standard output so that is can be easily used to feed it to another, Java based application. When no extra arguments are given, all defaults will be used, which can be influenced with the environment variables described above. @@ -163,19 +151,15 @@ When no extra arguments are given, all defaults will be used, which can be influ You can select specific sets of options by providing additional arguments: * `--debug` : Java debug options if `JAVA_DEBUG` is set -* `--memory` : Memory settings based on the environment variables given * `--proxy` : Evaluate proxy environments variables -* `--cpu` : Tuning when the number of cores is limited -* `--gc` : GC tuning parameters -* `--jit` : JIT options * `--diagnostics` : Print diagnostics options when `JAVA_DIAGNOSTICS` is set -* `--java-default` : Same as `--memory --jit --diagnostic --cpu --gc` +* `--java-default` : Defaults Example: ``` -# Call Maven with the proper memory settings when running in an container -export MAVEN_OPTS="$(run-java.sh options --memory)" +# Call Maven with the proper proxy settings when running in an container +export MAVEN_OPTS="$(run-java.sh options --proxy)" mvn clean install ``` diff --git a/images/jboss/openjdk8/jdk/run-java.sh b/images/jboss/openjdk8/jdk/run-java.sh index 1ebd872..23a973c 100644 --- a/images/jboss/openjdk8/jdk/run-java.sh +++ b/images/jboss/openjdk8/jdk/run-java.sh @@ -1,7 +1,8 @@ #!/bin/sh -# =================================================================================== -# Generic startup script for running arbitrary Java applications with -# being optimized for running in containers +# ============================================================================================================== +# Generic startup script for running arbitrary Java applications for JVM with support for -XX+UseContainerSupport +# and -XX:MaxRAMPercentage, -XX:MinRAMPercentage and -XX:InitialRAMPercentage +# for running in containers # # Usage: # # Execute a Java app: @@ -20,32 +21,16 @@ # Env-variables evaluated in this script: # # JAVA_OPTIONS: Checked for already set options -# JAVA_MAX_MEM_RATIO: Ratio use to calculate a default maximum Memory, in percent. -# E.g. the "50" value implies that 50% of the Memory -# given to the container is used as the maximum heap memory with -# '-Xmx'. -# It defaults to "25" when the maximum amount of memory available -# to the container is below 300M, otherwise defaults to "50". -# It is a heuristic and should be better backed up with real -# experiments and measurements. -# For a good overviews what tuning options are available --> -# https://youtu.be/Vt4G-pHXfs4 -# https://www.youtube.com/watch?v=w1rZOY5gbvk -# https://vimeo.com/album/4133413/video/181900266 -# Also note that heap is only a small portion of the memory used by a JVM. There are lot -# of other memory areas (metadata, thread, code cache, ...) which addes to the overall -# size. When your container gets killed because of an OOM, then you should tune -# the absolute values. -# JAVA_INIT_MEM_RATIO: Ratio use to calculate a default intial heap memory, in percent. -# By default this value is not set. -# -# The following variables are exposed to your Java application: -# -# CONTAINER_MAX_MEMORY: Max memory for the container (if running within a container) -# MAX_CORE_LIMIT: Number of cores available for the container (if running within a container) +# JVM values to set for heap size: +# -XX:MaxRAMPercentage +# -XX:MinRAMPercentage +# -XX:InitialRAMPercentage +# By default the values of Min and Max RAMPerdentage is set to 85.0 +# to allow the container's 85% of memory to be used for the max heap. +# Initial RAM Percentage is not set. -# ========================================================== +# ================================================================================================================= # Fail on a single failed command in a pipeline (if supported) (set -o | grep -q pipefail) && set -o pipefail @@ -122,71 +107,6 @@ find_jar_file() { fi } -# Generic formula evaluation based on awk -calc() { - local formula="$1" - shift - echo "$@" | awk ' - function ceil(x) { - return x % 1 ? int(x) + 1 : x - } - function log2(x) { - return log(x)/log(2) - } - function max2(x, y) { - return x > y ? x : y - } - function round(x) { - return int(x + 0.5) - } - {print '"int(${formula})"'} - ' -} - -# Based on the cgroup limits, figure out the max number of core we should utilize -core_limit() { - local cpu_period_file="/sys/fs/cgroup/cpu/cpu.cfs_period_us" - local cpu_quota_file="/sys/fs/cgroup/cpu/cpu.cfs_quota_us" - if [ -r "${cpu_period_file}" ]; then - local cpu_period="$(cat ${cpu_period_file})" - - if [ -r "${cpu_quota_file}" ]; then - local cpu_quota="$(cat ${cpu_quota_file})" - # cfs_quota_us == -1 --> no restrictions - if [ ${cpu_quota:-0} -ne -1 ]; then - echo $(calc 'ceil($1/$2)' "${cpu_quota}" "${cpu_period}") - fi - fi - fi -} - -max_memory() { - # High number which is the max limit until which memory is supposed to be - # unbounded. - local mem_file="/sys/fs/cgroup/memory/memory.limit_in_bytes" - if [ -r "${mem_file}" ]; then - local max_mem_cgroup="$(cat ${mem_file})" - local max_mem_meminfo_kb="$(cat /proc/meminfo | awk '/MemTotal/ {print $2}')" - local max_mem_meminfo="$(expr $max_mem_meminfo_kb \* 1024)" - if [ ${max_mem_cgroup:-0} != -1 ] && [ ${max_mem_cgroup:-0} -lt ${max_mem_meminfo:-0} ] - then - echo "${max_mem_cgroup}" - fi - fi -} - -init_limit_env_vars() { - # Read in container limits and export the as environment variables - local core_limit="$(core_limit)" - if [ -n "${core_limit}" ]; then - export CONTAINER_CORE_LIMIT="${core_limit}" - fi - - local mem_limit="$(max_memory)" - if [ -n "${mem_limit}" ]; then - export CONTAINER_MAX_MEMORY="${mem_limit}" - fi -} load_env() { local script_dir="$1" @@ -284,88 +204,31 @@ format_classpath() { # ========================================================================== memory_options() { - echo "$(calc_init_memory) $(calc_max_memory)" + echo "$(set_max_memory)" return } -# Check for memory options and set max heap size if needed -calc_max_memory() { +# Check for memory options and set max heap size as percentage of RAM, if needed +set_max_memory() { + local MaxRAMPercentage="" + local MinRAMPercentage="" + # Check whether -Xmx is already given in JAVA_OPTIONS if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xmx"; then return fi - - if [ -z "${CONTAINER_MAX_MEMORY:-}" ]; then - return - fi - - # Check for the 'real memory size' and calculate Xmx from the ratio - if [ -n "${JAVA_MAX_MEM_RATIO:-}" ]; then - if [ "${JAVA_MAX_MEM_RATIO}" -eq 0 ]; then - # Explicitely switched off - return - fi - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_MAX_MEM_RATIO}" "mx" - # When JAVA_MAX_MEM_RATIO not set and JVM >= 10 no max_memory - elif [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - elif [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - # Restore the one-fourth default heap size instead of the one-half below 300MB threshold - # See https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "25" "mx" - else - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "50" "mx" - fi -} - -# Check for memory options and set initial heap size if requested -calc_init_memory() { - # Check whether -Xms is already given in JAVA_OPTIONS. - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-Xms"; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MaxRAMPercentage"); then + MaxRAMPercentage="-XX:MaxRAMPercentage=85.0" fi - - # Check if value set - if [ -z "${JAVA_INIT_MEM_RATIO:-}" ] || [ -z "${CONTAINER_MAX_MEMORY:-}" ] || [ "${JAVA_INIT_MEM_RATIO}" -eq 0 ]; then - return + + if ! (echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:MinRAMPercentage"); then + MinRAMPercentage="-XX:MinRAMPercentage=85.0" fi - # Calculate Xms from the ratio given - calc_mem_opt "${CONTAINER_MAX_MEMORY}" "${JAVA_INIT_MEM_RATIO}" "ms" + echo "${MaxRAMPercentage} ${MinRAMPercentage}" } -calc_mem_opt() { - local max_mem="$1" - local fraction="$2" - local mem_opt="$3" - - local val=$(calc 'round($1*$2/100/1048576)' "${max_mem}" "${fraction}") - echo "-X${mem_opt}${val}m" -} - -c2_disabled() { - if [ -n "${CONTAINER_MAX_MEMORY:-}" ]; then - # Disable C2 compiler when container memory <=300MB - if [ "${CONTAINER_MAX_MEMORY}" -le 314572800 ]; then - echo true - return - fi - fi - echo false -} - -jit_options() { - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - # Check whether -XX:TieredStopAtLevel is already given in JAVA_OPTIONS - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:TieredStopAtLevel"; then - return - fi - if [ $(c2_disabled) = true ]; then - echo "-XX:TieredStopAtLevel=1" - fi -} # Switch on diagnostics except when switched off diagnostics_options() { @@ -378,72 +241,9 @@ diagnostics_options() { fi } -# Replicate thread ergonomics for tiered compilation. -# This could ideally be skipped when tiered compilation is disabled. -# The algorithm is taken from: -# OpenJDK / jdk8u / jdk8u / hotspot -# src/share/vm/runtime/advancedThresholdPolicy.cpp -ci_compiler_count() { - local core_limit="$1" - local log_cpu=$(calc 'log2($1)' "$core_limit") - local loglog_cpu=$(calc 'log2(max2($1,1))' "$log_cpu") - local count=$(calc 'max2($1*$2,1)*3/2' "$log_cpu" "$loglog_cpu") - local c1_count=$(calc 'max2($1/3,1)' "$count") - local c2_count=$(calc 'max2($1-$2,1)' "$count" "$c1_count") - [ $(c2_disabled) = true ] && echo "$c1_count" || echo $(calc '$1+$2' "$c1_count" "$c2_count") -} - -cpu_options() { - # JVMs >= 10 know about CPU limits - if [ "${JAVA_MAJOR_VERSION:-0}" -ge "10" ]; then - return - fi - - local core_limit="${JAVA_CORE_LIMIT:-}" - if [ "$core_limit" = "0" ]; then - return - fi - - if [ -n "${CONTAINER_CORE_LIMIT:-}" ]; then - if [ -z ${core_limit} ]; then - core_limit="${CONTAINER_CORE_LIMIT}" - fi - echo "-XX:ParallelGCThreads=${core_limit} " \ - "-XX:ConcGCThreads=${core_limit} " \ - "-Djava.util.concurrent.ForkJoinPool.common.parallelism=${core_limit} " \ - "-XX:CICompilerCount=$(ci_compiler_count $core_limit)" - fi -} - -#-XX:MinHeapFreeRatio=20 These parameters tell the heap to shrink aggressively and to grow conservatively. -#-XX:MaxHeapFreeRatio=40 Thereby optimizing the amount of memory available to the operating system. -heap_ratio() { - echo "-XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40" -} - -# These parameters are necessary when running parallel GC if you want to use the Min and Max Heap Free ratios. -# Skip setting gc_options if any other GC is set in JAVA_OPTIONS. -# -XX:GCTimeRatio=4 -# -XX:AdaptiveSizePolicyWeight=90 -gc_options() { - if echo "${JAVA_OPTIONS:-}" | grep -q -- "-XX:.*Use.*GC"; then - return - fi - - local opts="" - # for JVMs < 10 set GC settings - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-0}" -lt "10" ]; then - opts="${opts} -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 $(heap_ratio)" - fi - if [ -z "${JAVA_MAJOR_VERSION:-}" ] || [ "${JAVA_MAJOR_VERSION:-}" != "7" ]; then - opts="${opts} -XX:+ExitOnOutOfMemoryError" - fi - echo $opts -} - java_default_options() { # Echo options, trimming trailing and multiple spaces - echo "$(memory_options) $(jit_options) $(diagnostics_options) $(cpu_options) $(gc_options)" | awk '$1=$1' + echo "$(memory_options) $(diagnostics_options)" | awk '$1=$1' } @@ -564,21 +364,9 @@ options() { if [ $(hasflag --java-default) ]; then ret="$ret $(java_default_options)" fi - if [ $(hasflag --memory) ]; then - ret="$ret $(memory_options)" - fi - if [ $(hasflag --jit) ]; then - ret="$ret $(jit_options)" - fi if [ $(hasflag --diagnostics) ]; then ret="$ret $(diagnostics_options)" fi - if [ $(hasflag --cpu) ]; then - ret="$ret $(cpu_options)" - fi - if [ $(hasflag --gc) ]; then - ret="$ret $(gc_options)" - fi echo $ret | awk '$1=$1' } @@ -608,9 +396,6 @@ run() { # ============================================================================= # Fire up -# Set env vars reflecting limits -init_limit_env_vars - first_arg=${1:-} if [ "${first_arg}" = "options" ]; then # Print out options only @@ -622,4 +407,4 @@ elif [ "${first_arg}" = "run" ]; then # as first argument to your shift fi -run "$@" +run "$@" \ No newline at end of file