Last active
November 6, 2021 15:53
-
-
Save gburd/838e64fd9c6cfac1eaba6808c7dc794f to your computer and use it in GitHub Desktop.
A script to launch the Java VM (JVM) with some useful configrations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 ...