Skip to content

Instantly share code, notes, and snippets.

@gburd
Last active November 6, 2021 15:53
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gburd/838e64fd9c6cfac1eaba6808c7dc794f to your computer and use it in GitHub Desktop.
Save gburd/838e64fd9c6cfac1eaba6808c7dc794f to your computer and use it in GitHub Desktop.
A script to launch the Java VM (JVM) with some useful configrations.
#!/bin/bash
contains() {
string="$1"
substring="$2"
if test "${string#*$substring}" != "$string"
then
return 0 # $substring is in $string
else
return 1 # $substring is not in $string
fi
}
bytes() {
#echo $1 | echo $((`sed 's/.*/\L\0/;s/t/Xg/;s/g/Xm/;s/m/Xk/;s/k/X/;s/b//;s/X/ *1024/g'`))
echo $1 | echo $((`sed 's/[ ]*//g;s/[bB]$//;s/^.*[^0-9gkmt].*$//;s/t$/Xg/;s/g$/Xm/;s/m$/Xk/;s/k$/X/;s/X/*1024/g'`))
}
JMX_REMOTE_PORT=9999
if [ "X"$JMX_REMOTE_PORT == "X" ]; then
read lowerPort upperPort < /proc/sys/net/ipv4/ip_local_port_range
while :; do
for (( port = lowerPort ; port <= upperPort ; port++ )); do
nc -l -p "$port" 2>/dev/null && break 2
done
done
JMX_REMOTE_PORT=$port
fi
# Best Practice: "Heap size jitter"
# By introducing a bit of variation in the heap size across instances we can potentially
# avoid correlated OutOfMemory errors across all nodes under similar load on similar hardware
# which could lead to complete service outage.
if [[ -z "$HEAPSIZE" ]]; then
# Default to using 1/3 of the available physical RAM for our JVM heap
JVM_PCT_HEAP=${JVM_PCT_HEAP:=.33}
SEED=$RANDOM
if [[ -e /proc/meminfo ]]; then
HEAPSIZE=$(cat /proc/meminfo | awk -v pct=$JVM_PCT_HEAP -v seed=$SEED 'BEGIN{ srand(seed) } /MemTotal/{ p = sprintf("%.0f", ($2 * pct) / 1024 ) } END{ printf("%.0f\n", p - rand() % .1 * p) }')m
else
HEAPSIZE=$(sysctl hw.memsize | awk -F: -v pct=$JVM_PCT_HEAP -v seed=$SEED 'BEGIN{ srand(seed) } /hw.memsize/{ p = sprintf("%.0f", ($2 * pct) / (1024 * 1024) ) } END{ printf("%.0f\n", p - rand() % .1 * p) }')m
fi
fi
HEAPSIZE_UNCONVERTED=$HEAPSIZE
HEAPSIZE=$(bytes $HEAPSIZE)
#echo "$HEAPSIZE_UNCONVERTED -> $HEAPSIZE bytes"
TIMESTAMP=$(date +%Y%m%d%H%M%S)
LOG_DIR=${LOG_DIR:=/var/log/$(basename ${0})}
GC_LOG=${LOG_DIR}/jvm-gc-${TIMESTAMP}.log
HEAP_PATH=${LOG_DIR}/jvm-heap-${TIMESTAMP}.dump
if ! [ -d ${LOG_DIR} -a -w ${LOG_DIR} ]; then
LOG_DIR=.
GC_LOG=jvm-gc.log
HEAP_PATH=jvm-heap.dump
fi
jvm_args=(
# Use the HotSpot and other server-specific behaviors of the JVM.
-XX:+UseThreadPriorities
-XX:ThreadPriorityPolicy=42
# Caching network addresses too long can cause problems when migrating between VIPs.
-Dsun.net.inetaddr.ttl=60
-Dnetworkaddress.cache.ttl=60
-Dsun.net.inetaddr.negative.ttl=10
-Djava.net.preferIPv4Stack=true
# Enabled to allow us to generate a class histogram to debug memory usage and behavior.
# Simply send the JVM process a SIGQUIT (e.g., kill -3 PID_OF_JVM) to trigger this.
-XX:+PrintClassHistogram
# Enable JMX Remote access to allow VisualVM and other tools to inspect the running JVM process
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=${JMX_REMOTE_PORT}
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
# Primary JVM Memory Sizing
-Xmx${HEAPSIZE}
-Xms$(( ${HEAPSIZE} / 50 ))
-Xss1024k
-XX:MaxDirectMemorySize=$(( ${HEAPSIZE} / 10 ))
-XX:-UseAdaptiveSizePolicy
-XX:-OmitStackTraceInFastThrow
# Options for memory that aren't GC specific.
-verbose:gc
-Xloggc:${GC_LOG}
-XX:+UseCompressedOops
-XX:+OptimizeStringConcat
-XX:+UseStringDeduplication
-XX:+PrintStringDeduplicationStatistics
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=${HEAP_PATH}
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-XX:+PrintTenuringDistribution
-XX:+PrintGCApplicationStoppedTime
-XX:+DisableExplicitGC
-XX:+BindGCTaskThreadsToCPUs
-XX:+UseGCTaskAffinity
#-XX:ParallelGCThreads=$(( ${vCPU} / 2 ))
#-XX:ConcGCThreads=$(( ${vCPU} / 2 ))
-XX:+ParallelRefProcEnabled
-XX:+AlwaysPreTouch # allocate and zero (force fault) heap memory on startup
-XX:+UseTLAB # thread local allocation blocks
-XX:+ResizeTLAB # auto-optimize TLAB size
#-XX:-UseBiasedLocking
-XX:+UnlockExperimentalVMOptions
);
jvm_diagnostics_args=(
-XX:+UnlockDiagnosticVMOptions
-XX:+TraceClassLoading
-XX:+LogCompilation
-XX:+PrintAssembly
-XX:+DebugNonSafepoints
);
printf -v JVM_DIAGNOSTIC_ARGS "%s " "${jvm_diagnostic_args[@]}";
jvm_shenandoah_dynamic_gc_args=(
#-Xlog:gc
#-Xlog:gc+ergo
#-Xlog:gc+stats
-XX:+UseTransparentHugePages
-XX:+UseNUMA
-XX:+DisableExplicitGC
-XX:ShenandoahGCHeuristics=dynamic
-XX:ShenandoahFreeThreshold=60
-XX:ShenandoahAllocationThreshold=80
-XX:ShenandoahGarbageThreshold=10
);
jvm_g1_gc_args=(
-XX:+UseG1GC
-XX:MaxGCPauseMillis=500
-XX:G1MixedGCLiveThresholdPercent=10
-XX:G1RSetUpdatingPauseTimePercent=5
-XX:InitiatingHeapOccupancyPercent=25
);
jvm_cms_gc_args=(
-XX:+UseConcMarkSweepGC
-XX:ParGCCardsPerStrideChunk=4096
-XX:SurvivorRatio=2
-XX:MaxTenuringThreshold=16
-XX:+CMSScavengeBeforeRemark
-XX:CMSMaxAbortablePrecleanTime=60000
-XX:CMSWaitDuration=30000
-XX:CMSInitiatingOccupancyFraction=70
-XX:+UseCMSInitiatingOccupancyOnly
-Xmn$(( ${HEAPSIZE} / 25 )) # 1/4 to 1/3 the size of the heap
);
jvm_old_cms_young_parnew_gc_args=(
# GC Strategy: Parallel (ParNew) Concurrent Mark Sweep
-XX:+CMSClassUnloadingEnabled
# Young generation options
-XX:+UseParNewGC
-XX:NewSize=$(( ${HEAPSIZE} / 6 ))
-XX:MaxNewSize=$(( ${HEAPSIZE} / 6 ))
-XX:NewRatio=2
-XX:SurvivorRatio=3
-XX:MaxTenuringThreshold=15
-XX:ParGCCardsPerStrideChunk=4096
# Old generation options
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled
-XX:+CMSClassUnloadingEnabled
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=80
-XX:+ParallelRefProcEnabled
);
# Implode the general JVM arguments array into the new JVM_ARGS variable.
printf -v JVM_ARGS "%s " "${jvm_args[@]}";
# Implode the choice of garbage collection algorithm and tuning parameters arguments
# array into the new JVM_GC_ARGS variable.
case "$1" in
-gc:g1*)
shift
printf -v JVM_GC_ARGS "%s " "${jvm_g1_gc_args[@]}";
;;
-gc:cm*|cms)
shift
printf -v JVM_GC_ARGS "%s " "${jvm_cms_args[@]}";
;;
-gc:sh*)
shift
printf -v JVM_GC_ARGS "%s " "${jvm_shenandoah_dynamic_gc_args[@]}";
# NOTE: Huge page support on Linux requires:
# Ubuntu:
# apt install sysfsutils
# cat > /etc/sysfs.conf <<EOF
# kernel/mm/transparent_hugepage/enabled = madvise
# kernel/mm/transparent_hugepage/defrag = madvise
# EOF
if [ "$(cat /sys/kernel/mm/transparent_hugepage/enabled)" != "always [madvise] never" ]; then
echo "Huge page support is not configured for JVM but not enabled in kernel."
exit -1
fi
if [ "$(cat /sys/kernel/mm/transparent_hugepage/defrag)" != "always defer defer+madvise [madvise] never" ]; then
echo "Huge page defrag is configured for JVM but not enabled in kernel."
exit -1
fi
;;
-gc:pa*|-gc:pn*)
shift
printf -v JVM_GC_ARGS "%s " "${jvm_old_cms_young_parnew_gc_args[@]}";
;;
*)
JVM_GC_ARGS=""
;;
esac
# Compressed pointers only workes when heaps are < 4GiB
contains $JVM_ARGS "-XX:+UseCompressedOops"; if [ $? -ne 0 -a $HEAPSIZE -ge 4294967296 ]; then
JVM_ARGS=$(echo $JVM_ARGS | sed -e 's/-XX:+UseCompressedOops/-XX:-UseCompressedOops/')
fi
if [ "$1" == "-version" ]; then
# -XX:+PrintFlagsWithComments only works with debug build of JVM
echo java $JVM_ARGS $JVM_GC_ARGS -XX:+PrintFlagsFinal -version
java $JVM_ARGS $JCM_GC_ARGS -XX:+PrintFlagsFinal -version
else
if [ "$1" == "-diag" ]; then
JVM_ARGS="$JVM_ARGS $JVM_DIAGNOSTIC_ARGS"
shift
fi
if [ "$1" == "-server" ]; then
JVM_ARGS="$JVM_ARGS -server -ea -d64 -da -dsa"
shift
fi
echo java -server $JVM_ARGS $JVM_GC_ARGS -XX:OnError="gdb - %p" -XX:OnOutOfMemoryError='/bin/kill -15 %p' $@
java -server $JVM_ARGS $JVM_GC_ARGS -XX:OnError="gdb - %p" -XX:OnOutOfMemoryError='/bin/kill -15 %p' $@
fi
# JVM NOTES:
#
# Heap dump: jmap -F -dump:format=b,file=/tmp/dump.hprof $JVM_PID
# Heap layout: jmap -heap $JVM_PID
#
# To find memory leaks: (https://gdstechnology.blog.gov.uk/2015/12/11/using-jemalloc-to-get-to-the-bottom-of-a-memory-leak/ https://github.com/jeffgriffith/native-jvm-leaks/)
# export LD_PRELOAD=/usr/local/lib/libjemalloc.so
# or on macOS
# DYLD_INSERT_LIBRARIES=/usr/local/lib/libjemalloc.dylib DYLD_FORCE_FLAT_NAMESPACE=y
# export MALLOC_CONF=prof:true,lg_prof_interval:30,lg_prof_sample:17
# jeprof --show_bytes --gif /path/to/jvm/bin/java jeprof*.heap > /tmp/app-profiling.gif
@gburd
Copy link
Author

gburd commented Sep 15, 2017

To choose a GC you would: jvm.sh -gc:ga ...
To set the percent of the available RAM to use: env JVM_PCT_HEAP=.5 jvm.sh ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment