When analyzing Linux system performance, we often encounter a puzzling scenario where mpstat
reports high CPU utilization while /proc/loadavg
shows surprisingly low numbers. Let's break down the key components:
# Sample mpstat output (60-second interval)
mpstat -P ALL 60 1
# Typical load average check
cat /proc/loadavg
In your 24-logical-core system (12 cores × 2 threads), a Java process showing 2000% CPU in top
indicates heavy thread utilization. Consider this simplified view:
# Thread-centric view from top
top -H -p $(pgrep -f java)
# Alternative thread monitoring
pidstat -t -p $(pgrep -f java) 60 1
The 75% CPU utilization reported by mpstat
suggests your threads are efficiently using available CPU resources without significant queueing - explaining the low load average.
Linux load average represents:
- Runnable processes (state R in
ps
) - Uninterruptible processes (state D)
- Recently run processes (within the sampling interval)
# Check process states contributing to load
ps -eo state,cmd | awk '$1 ~ /[RD]/ {print $0}' | sort | uniq -c
To diagnose similar cases:
# 1. Check CPU run queue length
vmstat 1 5
# 2. Examine thread contention
jstack $(pgrep -f java) | grep -A 1 "java.lang.Thread.State: RUNNABLE"
# 3. Monitor context switches
pidstat -w -p $(pgrep -f java) 1 5
# 4. Check for CPU affinity issues
taskset -p $(pgrep -f java)
For web applications handling 100+ RPS:
// Example JVM tuning flags for thread-heavy workloads
-Djdk.nio.maxCachedBufferSize=262144
-XX:ParallelGCThreads=6
-XX:ConcGCThreads=3
-XX:+UseG1GC
-XX:InitiatingHeapOccupancyPercent=35
Remember to profile with tools like jvisualvm
or async-profiler
before applying optimizations.
The RHEL 6.3 kernel (2.6.32) handles CPU accounting differently than newer versions:
# Check scheduler statistics
cat /proc/schedstat
# Examine CPU migration patterns
cat /proc/[pid]/sched
Consider upgrading to a newer kernel if possible, as CPU load calculation improvements were made in later versions.
When we see 75% CPU utilization with only 2-4 load average on a 24-logical-core system, we're witnessing a classic case where traditional monitoring tools don't tell the full story. The key insight here is that Linux's load average measures runnable processes, while CPU utilization measures actual execution time.
Given this is a Java web application environment, the most likely culprit is thread contention. Here's how to verify:
# Check for thread contention in Java
jstack [pid] | grep -A 1 "java.lang.Thread.State: BLOCKED" | wc -l
# Monitor context switches
vmstat -w 2 5
What we often find in such cases is that application threads are:
- Blocked on synchronization (synchronized blocks, locks)
- Waiting on I/O that doesn't show as system time
- Contending for CPU cache lines (false sharing)
With 12 physical cores and hyperthreading, NUMA (Non-Uniform Memory Access) effects become significant. Check NUMA stats:
numastat -p [pid]
numactl --hardware
Sample output interpretation:
Per-node process memory usage (in MBs)
PID Node 0 Node 1 Total
--------------- ------------ ------------ ------------
12345 1024.12 512.34 1536.46
Standard tools like mpstat and /proc/loadavg don't show the complete picture. Consider these alternatives:
# Perf for CPU cycle analysis
perf stat -e cycles,instructions,cache-misses -p [pid]
# More detailed CPU breakdown
pidstat -t -p [pid] 2 5
# Kernel scheduler stats
cat /proc/schedstat | grep "cpu#"
For Java applications specifically, these JVM-level checks are valuable:
# Check JVM thread states
jcmd [pid] Thread.print
# Monitor lock contention
jcmd [pid] VM.print_threads
# Sample output showing blocked threads
"pool-1-thread-3" #17 prio=5 os_prio=0 tid=0x00007f48740b4000 nid=0x4d1b waiting for monitor entry [0x00007f486b7fe000]
Remember that in hyperthreaded environments, CPU utilization can appear high while actual throughput remains limited by physical core contention.
A common pattern we see in web applications is database connection pool contention. Here's how to identify it:
# Check for connection wait time in logs
grep "Connection obtained in" application.log
# JDBC connection pool monitoring (Tomcat example)
JMX: Catalina:type=DataSource,context=/yourapp,host=localhost,class=javax.sql.DataSource,name="jdbc/yourDB"
The solution often involves either increasing the pool size or reducing connection hold time through better transaction management.