Skip to main content
Advertisement

Pro Tips — JVM Tuning, GC Analysis, Tomcat Native

This chapter covers real-world techniques used by Tomcat operations experts: JVM heap sizing guidelines, GC log analysis, and Tomcat Native (APR) installation for performance gains.


JVM Heap Memory Tuning

Basic Principle

Recommended heap size = available memory × 0.5 ~ 0.7

Examples:
- 4GB RAM → -Xms1g -Xmx2g
- 8GB RAM → -Xms2g -Xmx4g
- 16GB RAM → -Xms4g -Xmx8g
- 32GB RAM → -Xms8g -Xmx16g

setenv.sh — Memory Configuration

# $CATALINA_HOME/bin/setenv.sh

# === Heap memory ===
export CATALINA_OPTS="$CATALINA_OPTS -Xms2g -Xmx4g"

# === GC algorithm ===
# Java 17+: G1GC (default, suitable for most cases)
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseG1GC"

# G1GC tuning
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxGCPauseMillis=200"
export CATALINA_OPTS="$CATALINA_OPTS -XX:G1HeapRegionSize=16m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:InitiatingHeapOccupancyPercent=45"

# === GC log ===
export CATALINA_OPTS="$CATALINA_OPTS \
-Xlog:gc*:file=$CATALINA_HOME/logs/gc.log:time,uptime,pid:filecount=5,filesize=20m"

# === Auto heap dump on OOM ===
export CATALINA_OPTS="$CATALINA_OPTS \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=$CATALINA_HOME/logs/heapdump.hprof"

# === Auto restart on OOM ===
export CATALINA_OPTS="$CATALINA_OPTS \
-XX:OnOutOfMemoryError='systemctl restart tomcat'"

# === Server mode ===
export CATALINA_OPTS="$CATALINA_OPTS -server"

# === Timezone / Encoding ===
export JAVA_OPTS="$JAVA_OPTS -Duser.timezone=Asia/Seoul -Dfile.encoding=UTF-8"

GC Algorithm Selection Guide

GCJava VersionCharacteristicsBest For
G1GCJava 9+ (default)Balanced throughput & latencyMost server applications
ZGCJava 15+ (stable)Ultra-low latency (< 1ms)Latency-sensitive services
ShenandoahJava 12+ (OpenJDK)Ultra-low latency, high throughputLarge heaps (> 8GB)
SerialGCAll versionsSingle-threadedDevelopment/testing
ParallelGCAll versionsMaximum throughputBatch processing

ZGC Configuration Example (Latency-Sensitive)

# ZGC — very low Stop-the-World (< 1ms)
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseZGC"
export CATALINA_OPTS="$CATALINA_OPTS -XX:SoftMaxHeapSize=6g"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx8g"

GC Log Analysis

Understanding GC Log Format

# Java 17+ GC log example (G1GC)
[0.123s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 256M->100M(512M) 8.234ms
[0.456s][info][gc] GC(1) Pause Young (Concurrent Start) (G1 Humongous Allocation) 400M->200M(512M) 15.678ms
[1.234s][info][gc] GC(2) Pause Full (G1 Compaction Pause) 500M->300M(512M) 1250.123ms

Analysis Points

Pause Young (Normal)    → Normal Young GC, < 50ms recommended
Pause Young (Concurrent Start) → Concurrent marking starts (Old GC sign)
Pause Full → Full GC! Investigate (insufficient heap or memory leak)

GC Log Analysis Tools

# 1. GCEasy.io (online) — upload GC log file

# 2. GCViewer (open source)
java -jar gcviewer-1.36.jar /opt/tomcat/latest/logs/gc.log

# 3. Count Full GC occurrences
grep "Pause Full" /opt/tomcat/latest/logs/gc.log | wc -l

# 4. Average GC pause time
grep "Pause Young" /opt/tomcat/latest/logs/gc.log | \
awk -F'ms' '{print $(NF-1)}' | \
awk '{sum+=$1; cnt++} END {print "Avg:", sum/cnt "ms"}'

Tomcat Native (APR) — Native Performance

The Tomcat Native library uses OpenSSL and APR (Apache Portable Runtime) to perform SSL processing and file I/O at the native level.

Performance Improvements

ItemDefault Java NIOTomcat Native (APR)
SSL HandshakeJava JSSEOpenSSL (faster)
Static file servingJava File APIsendfile system call
Connection handlingJava NIOAPR native I/O
Recommended forGeneral web appsHigh-traffic file serving + SSL

Modern perspective: If Tomcat runs behind Nginx as a backend, Nginx handles SSL, so Tomcat Native's practical benefit is limited. It's most useful when Tomcat handles SSL directly or serves large files.

Installing Tomcat Native on Ubuntu

# Install dependencies
sudo apt install -y libapr1-dev libssl-dev gcc make

# Download Tomcat Native source
cd /tmp
wget https://dlcdn.apache.org/tomcat/tomcat-connectors/native/2.0.7/source/tomcat-native-2.0.7-src.tar.gz
tar -xzf tomcat-native-2.0.7-src.tar.gz
cd tomcat-native-2.0.7-src/native

# Compile and install
./configure \
--with-apr=/usr/bin/apr-1-config \
--with-java-home=$JAVA_HOME \
--with-ssl=yes \
--prefix=/opt/tomcat/latest

make && sudo make install

# Verify installation
ls /opt/tomcat/latest/lib/libtcnative*

Add Native Library Path to setenv.sh

export LD_LIBRARY_PATH="/opt/tomcat/latest/lib:$LD_LIBRARY_PATH"
export CATALINA_OPTS="$CATALINA_OPTS \
-Djava.library.path=/opt/tomcat/latest/lib"

Common Issues and Solutions

Issue 1: OutOfMemoryError: Java heap space

# Symptom
java.lang.OutOfMemoryError: Java heap space

# Diagnosis
# Check heap usage in real-time
jstat -gcutil <PID> 1000 10

# Analyze heap dump with Eclipse MAT
jmap -dump:format=b,file=/tmp/heapdump.hprof <PID>

# Fix: increase -Xmx in setenv.sh
export CATALINA_OPTS="$CATALINA_OPTS -Xms4g -Xmx8g"

Issue 2: Metaspace OOM

# Symptom
java.lang.OutOfMemoryError: Metaspace

# Fix: set max Metaspace size
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=512m"

# Enable class unloading
export CATALINA_OPTS="$CATALINA_OPTS -XX:+ClassUnloadingWithConcurrentMark"

Issue 3: Thread Starvation

# Symptom: HTTP 503, catalina.out shows:
# WARNING: All available threads (200) are currently busy

# Generate thread dump
kill -3 <TOMCAT_PID>

# Or use jstack
jstack <PID> > /tmp/thread-dump.txt

# Analyze: many BLOCKED threads indicate lock contention or DB pool exhaustion
grep "BLOCKED" /tmp/thread-dump.txt | wc -l

# Fix: increase maxThreads or DB pool size

Issue 4: ClassLoader Memory Leak After Redeployment

# Symptom: Metaspace grows progressively after repeated redeployments

# Check warning messages
grep "The web application \[" /opt/tomcat/latest/logs/catalina.out
# WARNING: The web application [...] appears to have started a thread
# named [xxx] but has failed to stop it.

# Fix:
# 1. Ensure ThreadLocal.remove() in finally blocks
# 2. Shutdown thread pools on application stop
# 3. Clean up resources in ServletContextListener

Operations Health Check Script

#!/bin/bash
echo "=== Tomcat Health Check ==="

# 1. Process check
if ! pgrep -f "catalina" > /dev/null; then
echo "❌ Tomcat process not running"
exit 1
fi

# 2. HTTP response check
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/)
if [ "$HTTP_CODE" != "200" ]; then
echo "❌ Abnormal HTTP response: $HTTP_CODE"
exit 1
fi

# 3. Heap usage check
PID=$(pgrep -f catalina)
HEAP_USED=$(jstat -gcutil $PID | tail -1 | awk '{print $4}')
echo "✅ Old Gen usage: ${HEAP_USED}%"
if (( $(echo "$HEAP_USED > 85" | bc -l) )); then
echo "⚠️ Heap usage warning: ${HEAP_USED}%"
fi

echo "✅ All checks passed"

Summary

ItemRecommended Setting
Heap size-Xms = -Xmx = available RAM × 0.5~0.7
GC algorithmJava 17+: G1GC (default), ZGC for low-latency
GC log-Xlog:gc*:file=gc.log:filecount=5,filesize=20m
OOM handling-XX:+HeapDumpOnOutOfMemoryError
Tomcat NativeWhen handling SSL directly or high-traffic file serving
DB pool sizemaxTotal = maxThreads × 0.5~0.8
Advertisement