Introduction
Core dump analysis is crucial for diagnosing complex Java application issues like crashes, memory leaks, and performance problems. The jcmd tool, introduced in JDK 7, provides comprehensive diagnostic capabilities for live JVM processes and core dump analysis.
jcmd Fundamentals
Basic jcmd Syntax and Usage
# List all Java processes jcmd -l # Display available commands for a specific process jcmd <pid> help # Get basic process information jcmd <pid> VM.version jcmd <pid> VM.command_line jcmd <pid> VM.flags # Thread information jcmd <pid> Thread.print jcmd <pid> VM.threads
Core Dump Generation
Creating Core Dumps
# Generate core dump for running Java process jcmd <pid> GC.heap_dump /path/to/heapdump.hprof jcmd <pid> VM.native_memory summary jcmd <pid> VM.native_memory baseline jcmd <pid> VM.native_memory summary.diff # On Unix/Linux systems - generate system core dump ulimit -c unlimited kill -SIGABRT <pid> # JVM options for core dump configuration java -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=/path/to/dumps \ -XX:OnError="gcore %p" \ -jar application.jar # Generate multiple diagnostic files jcmd <pid> PerfCounter.print jcmd <pid> VM.system_properties jcmd <pid> VM.uptime
Comprehensive jcmd Diagnostic Commands
Memory Analysis Commands
# Heap histogram jcmd <pid> GC.class_histogram # Detailed heap histogram jcmd <pid> GC.class_histogram -all # Run garbage collection jcmd <pid> GC.run # Get heap usage jcmd <pid> GC.heap_info # Native memory tracking jcmd <pid> VM.native_memory detail jcmd <pid> VM.native_memory scale=MB
Thread and Performance Analysis
# Thread dump with additional information jcmd <pid> Thread.print -l # JMX data jcmd <pid> ManagementAgent.status jcmd <pid> ManagementAgent.start_local # Performance counters jcmd <pid> PerfCounter.print # Compiler information jcmd <pid> Compiler.queue jcmd <pid> Compiler.directives_print
Advanced Core Dump Analysis
Heap Dump Analysis
// Example Java application that might cause memory issues
public class MemoryLeakExample {
private static final List<byte[]> LEAKING_LIST = new ArrayList<>();
private static final Map<String, String> CACHE = new ConcurrentHashMap<>();
public static void main(String[] args) {
// Simulate memory leak
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
// Intentionally leak memory
LEAKING_LIST.add(new byte[1024 * 1024]); // 1MB chunks
CACHE.put(UUID.randomUUID().toString(), "Some cached data");
System.out.println("Allocated: " + (LEAKING_LIST.size()) + " MB");
}, 0, 1, TimeUnit.SECONDS);
// Keep application running
try {
Thread.sleep(300000); // 5 minutes
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
Analysis Script for Core Dumps
#!/bin/bash # comprehensive_dump_analysis.sh PID=$1 DUMP_DIR="/tmp/java_dumps_$(date +%Y%m%d_%H%M%S)" mkdir -p $DUMP_DIR echo "Starting comprehensive analysis for PID: $PID" echo "Output directory: $DUMP_DIR" # Basic JVM information jcmd $PID VM.version > $DUMP_DIR/vm_version.txt jcmd $PID VM.command_line > $DUMP_DIR/vm_command_line.txt jcmd $PID VM.flags > $DUMP_DIR/vm_flags.txt # System properties jcmd $PID VM.system_properties > $DUMP_DIR/system_properties.txt # Memory information jcmd $PID GC.heap_info > $DUMP_DIR/heap_info.txt jcmd $PID VM.native_memory summary > $DUMP_DIR/native_memory_summary.txt # Thread analysis jcmd $PID Thread.print -l > $DUMP_DIR/thread_dump.txt jcmd $PID VM.threads > $DUMP_DIR/vm_threads.txt # Class histogram jcmd $PID GC.class_histogram -all > $DUMP_DIR/class_histogram.txt # Performance counters jcmd $PID PerfCounter.print > $DUMP_DIR/perf_counters.txt # Generate heap dump jcmd $PID GC.heap_dump $DUMP_DIR/heap_dump.hprof echo "Analysis complete. Files available in: $DUMP_DIR"
Native Memory Tracking
Enabling and Using NMT
# Start JVM with Native Memory Tracking java -XX:NativeMemoryTracking=detail \ -XX:+UnlockDiagnosticVMOptions \ -XX:+PrintNMTStatistics \ -jar application.jar # Monitor native memory jcmd <pid> VM.native_memory baseline jcmd <pid> VM.native_memory summary.diff jcmd <pid> VM.native_memory detail.diff jcmd <pid> VM.native_memory scale=KB # Example output analysis echo "=== Native Memory Tracking Summary ===" jcmd <pid> VM.native_memory summary
NMT Analysis Script
#!/bin/bash
# nmt_monitor.sh
PID=$1
INTERVAL=${2:-10}
echo "Monitoring Native Memory for PID: $PID every ${INTERVAL}s"
echo "Time,Total,Java_Heap,Class,Thread,Code,GC,Compiler,Internal,Symbol,Native_Memory_Tracking"
while true; do
TIMESTAMP=$(date +%H:%M:%S)
jcmd $PID VM.native_memory summary | awk -v ts="$TIMESTAMP" '
/Total: / {total=$2}
/Java Heap \(reserved=/ {heap=$3}
/Class \(reserved=/ {class=$3}
/Thread \(reserved=/ {thread=$3}
/Code \(reserved=/ {code=$3}
/GC \(reserved=/ {gc=$3}
/Compiler \(reserved=/ {compiler=$3}
/Internal \(reserved=/ {internal=$3}
/Symbol \(reserved=/ {symbol=$3}
/Native Memory Tracking \(reserved=/ {nmt=$4}
END {
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
ts, total, heap, class, thread, code, gc, compiler, internal, symbol, nmt
}'
sleep $INTERVAL
done
Thread Dump Analysis
Advanced Thread Analysis
#!/bin/bash
# thread_analysis.sh
PID=$1
COUNT=${2:-5}
INTERVAL=${3:-10}
echo "Capturing $COUNT thread dumps for PID: $PID with ${INTERVAL}s interval"
for i in $(seq 1 $COUNT); do
echo "=== Thread Dump $i at $(date) ===" >> thread_dumps_$PID.txt
jcmd $PID Thread.print -l >> thread_dumps_$PID.txt
echo -e "\n\n" >> thread_dumps_$PID.txt
if [ $i -lt $COUNT ]; then
sleep $INTERVAL
fi
done
echo "Thread dumps saved to thread_dumps_$PID.txt"
# Analyze common thread issues
echo "=== Thread State Analysis ===" > thread_analysis_$PID.txt
grep "java.lang.Thread.State:" thread_dumps_$PID.txt | sort | uniq -c >> thread_analysis_$PID.txt
echo "=== Waiting Threads ===" >> thread_analysis_$PID.txt
grep -A 2 "WAITING" thread_dumps_$PID.txt >> thread_analysis_$PID.txt
echo "=== Blocked Threads ===" >> thread_analysis_$PID.txt
grep -A 2 "BLOCKED" thread_dumps_$PID.txt >> thread_analysis_$PID.txt
Performance Counter Analysis
Monitoring Performance Metrics
#!/bin/bash
# performance_monitor.sh
PID=$1
DURATION=${2:-300}
INTERVAL=${3:-5}
END_TIME=$((SECONDS + DURATION))
OUTPUT_FILE="performance_${PID}_$(date +%Y%m%d_%H%M%S).csv"
echo "Monitoring performance for PID: $PID for ${DURATION}s"
echo "Timestamp,HeapUsed,HeapCommitted,GC_Time,Loaded_Classes,Active_Threads" > $OUTPUT_FILE
while [ $SECONDS -lt $END_TIME ]; do
TIMESTAMP=$(date +%H:%M:%S)
jcmd $PID PerfCounter.print | awk -v ts="$TIMESTAMP" '
/java.heap.used/ {heap_used=$2}
/java.heap.committed/ {heap_committed=$2}
/sun.gc.collector.0.time/ {gc_time=$2}
/java.cls.loadedClasses/ {loaded_classes=$2}
/java.threads.live/ {active_threads=$2}
END {
printf "%s,%s,%s,%s,%s,%s\n",
ts, heap_used, heap_committed, gc_time, loaded_classes, active_threads
}' >> $OUTPUT_FILE
sleep $INTERVAL
done
echo "Performance data saved to: $OUTPUT_FILE"
Memory Leak Detection
Heap Analysis Automation
#!/bin/bash # memory_leak_detector.sh PID=$1 BASELINE_DIR="/tmp/memory_baseline_$PID" if [ ! -d "$BASELINE_DIR" ]; then echo "Creating baseline..." mkdir -p $BASELINE_DIR jcmd $PID GC.heap_dump $BASELINE_DIR/baseline.hprof jcmd $PID GC.class_histogram -all > $BASELINE_DIR/baseline_histogram.txt echo "Baseline created in: $BASELINE_DIR" else echo "Comparing with baseline..." CURRENT_DIR="/tmp/memory_current_$PID" mkdir -p $CURRENT_DIR jcmd $PID GC.heap_dump $CURRENT_DIR/current.hprof jcmd $PID GC.class_histogram -all > $CURRENT_DIR/current_histogram.txt # Simple diff analysis echo "=== Class Histogram Differences ===" > $CURRENT_DIR/comparison.txt diff $BASELINE_DIR/baseline_histogram.txt $CURRENT_DIR/current_histogram.txt >> $CURRENT_DIR/comparison.txt echo "Analysis complete. Check: $CURRENT_DIR/comparison.txt" fi
JVM Troubleshooting Scenarios
OutOfMemoryError Analysis
#!/bin/bash
# oom_analysis.sh
PID=$1
echo "=== OutOfMemoryError Diagnostic ==="
echo "Timestamp: $(date)"
# Basic JVM info
echo -e "\n=== JVM Version ==="
jcmd $PID VM.version
echo -e "\n=== JVM Flags ==="
jcmd $PID VM.flags | grep -i heap
echo -e "\n=== Heap Usage ==="
jcmd $PID GC.heap_info
echo -e "\n=== Class Histogram (Top 20) ==="
jcmd $PID GC.class_histogram | head -20
echo -e "\n=== Native Memory ==="
jcmd $PID VM.native_memory summary
echo -e "\n=== GC Statistics ==="
jcmd $PID GC.run
jcmd $PID GC.heap_info
echo -e "\n=== Generating Heap Dump ==="
DUMP_FILE="/tmp/oom_analysis_${PID}_$(date +%Y%m%d_%H%M%S).hprof"
jcmd $PID GC.heap_dump $DUMP_FILE
echo "Heap dump saved to: $DUMP_FILE"
Deadlock Detection
#!/bin/bash
# deadlock_detector.sh
PID=$1
echo "Checking for deadlocks in PID: $PID"
# Thread dump analysis for deadlocks
jcmd $PID Thread.print -l | awk '
BEGIN { deadlock_found=0; in_deadlock=0 }
/Found.*deadlock/ {
deadlock_found=1;
in_deadlock=1;
print "=== DEADLOCK DETECTED ==="
next
}
/Found [0-9]+ deadlock/ {
deadlock_found=1;
in_deadlock=1;
print "=== DEADLOCK DETECTED ==="
next
}
in_deadlock && /^$/ { in_deadlock=0 }
in_deadlock { print }
END {
if (!deadlock_found) {
print "No deadlocks found"
}
}'
Automated Monitoring System
Comprehensive Monitoring Script
#!/bin/bash
# jvm_health_monitor.sh
PID=$1
MONITOR_DIR="/tmp/jvm_monitor_${PID}_$(date +%Y%m%d_%H%M%S)"
mkdir -p $MONITOR_DIR
echo "Starting JVM health monitor for PID: $PID"
echo "Output directory: $MONITOR_DIR"
# Continuous monitoring loop
while true; do
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# Basic health checks
{
echo "=== Health Check at $(date) ==="
echo "=== Thread States ==="
jcmd $PID Thread.print -l | grep "java.lang.Thread.State:" | sort | uniq -c
echo -e "\n=== Memory Usage ==="
jcmd $PID GC.heap_info
echo -e "\n=== GC Activity ==="
jcmd $PID PerfCounter.print | grep -E "(gc.|GC.)"
echo -e "\n=== Loaded Classes ==="
jcmd $PID PerfCounter.print | grep "loadedClasses"
} > "$MONITOR_DIR/health_check_${TIMESTAMP}.txt"
# Check for critical conditions
THREAD_COUNT=$(jcmd $PID PerfCounter.print | grep "java.threads.live" | awk '{print $2}')
HEAP_USED=$(jcmd $PID GC.heap_info | grep "used" | awk '{print $3}' | sed 's/K//')
# Alert conditions
if [ $THREAD_COUNT -gt 1000 ]; then
echo "WARNING: High thread count: $THREAD_COUNT" >> "$MONITOR_DIR/alerts.txt"
fi
if [ $HEAP_USED -gt 4000000 ]; then # 4GB threshold
echo "WARNING: High heap usage: $HEAP_USED K" >> "$MONITOR_DIR/alerts.txt"
# Generate emergency heap dump
jcmd $PID GC.heap_dump "$MONITOR_DIR/emergency_dump_${TIMESTAMP}.hprof"
fi
sleep 60 # Check every minute
done
Analysis Tools Integration
Integrating with Other Tools
#!/bin/bash
# integrated_analysis.sh
PID=$1
echo "=== Integrated JVM Analysis ==="
# Use jcmd with other JDK tools
echo -e "\n=== jstat GC Analysis ==="
jstat -gc $PID 1000 5
echo -e "\n=== jstack Thread Analysis ==="
jstack $PID > /tmp/jstack_$PID.txt
echo -e "\n=== jmap Heap Summary ==="
jmap -heap $PID
# Generate comprehensive report
{
echo "Comprehensive JVM Analysis Report"
echo "Generated: $(date)"
echo "PID: $PID"
echo -e "\n--- jcmd VM Information ---"
jcmd $PID VM.version
jcmd $PID VM.flags
echo -e "\n--- Memory Analysis ---"
jcmd $PID GC.heap_info
jcmd $PID VM.native_memory summary
echo -e "\n--- Thread Analysis ---"
jcmd $PID Thread.print -l | head -50
} > /tmp/jvm_analysis_report_$PID.txt
echo "Report generated: /tmp/jvm_analysis_report_$PID.txt"
Best Practices for Production
Production Monitoring Setup
#!/bin/bash # production_monitor_setup.sh # Set up core dump configuration echo "Configuring system for core dumps..." ulimit -c unlimited echo "core.%p" > /proc/sys/kernel/core_pattern # JVM options for production monitoring JVM_OPTS=" -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/java/heapdumps -XX:ErrorFile=/var/log/java/hs_err_pid%p.log -XX:NativeMemoryTracking=detail -XX:+UnlockDiagnosticVMOptions -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/var/log/java/gc.log " echo "Add these JVM options for production monitoring:" echo "$JVM_OPTS" # Create monitoring directory mkdir -p /var/log/java/heapdumps chmod 755 /var/log/java/heapdumps echo "Production monitoring setup complete"
Conclusion
Core dump analysis with jcmd provides powerful insights into JVM behavior and application issues. Key takeaways:
- jcmd offers comprehensive diagnostics for live processes and core dumps
- Native Memory Tracking helps identify memory issues outside the Java heap
- Regular monitoring can prevent production incidents
- Automated scripts enable proactive issue detection
- Integration with other JDK tools provides complete visibility
By mastering these techniques, developers and operations teams can effectively diagnose and resolve complex Java application issues in production environments.