Understanding and Optimizing High Committed Memory vs Application Memory in Linux Servers (MySQL/Node.js/Apache Case Study)


2 views

When analyzing your Munin memory graph, three key metrics stand out:

apps ............. 0.7G (actual physical memory used by applications)
cached ........... varies (disk cache using free memory)
committed ........ ~1.5G (green line, what the kernel has promised to processes)

Committed memory represents the total memory the kernel has "promised" to all processes, including:

  • Currently allocated physical RAM
  • Potential future allocations (even if not yet used)
  • Memory that could be swapped out

In your case with multiple services running:

# Check per-process memory with smem
sudo smem -t -k -P "mysql|node|apache|nginx|jenkins"
PID User     Command                         Swap      USS      PSS      RSS 
1234 mysql    /usr/sbin/mysqld               72.3M    312.4M   328.1M   412.8M
5678 node     /usr/bin/node app.js           48.1M    158.2M   163.4M   201.5M

Linux starts swapping when:

Committed Memory > (Physical RAM + Swap Space) * vm.overcommit_ratio

Check your current thresholds:

cat /proc/sys/vm/swappiness
cat /proc/sys/vm/overcommit_ratio

For MySQL (often the biggest consumer):

# In my.cnf
[mysqld]
innodb_buffer_pool_size = 256M  # Reduce from default
table_open_cache = 400          # Down from 2000

Node.js Memory Limits:

# Launch Node with explicit memory limit
node --max-old-space-size=384 app.js

Apache/Nginx Tuning:

# Apache (prefork)
MaxRequestWorkers 20           # Down from 150
# Nginx
worker_processes 2;            # Match CPU cores
worker_connections 768;        # Down from 1024

Consider cgroups for strict memory containment:

# Create memory-limited group
cgcreate -g memory:/limited_group
echo 800M > /sys/fs/cgroup/memory/limited_group/memory.limit_in_bytes

# Run Jenkins in the constrained group
cgexec -g memory:limited_group java -jar jenkins.war

html

When monitoring server performance with Munin, the "committed memory" metric (green line) often causes confusion. Unlike "apps" memory (0.7G/1.5G in your case), committed memory represents the total memory promised to processes by the Linux kernel - including both currently used memory and potential future allocations.

Your setup running MySQL, Node.js, Apache, and Nginx simultaneously creates competing memory demands:

# Check current memory commitments
$ grep Commit /proc/meminfo
CommitLimit:     1548284 kB
Committed_AS:    1423568 kB

When Committed_AS approaches CommitLimit (as in your graph), the kernel starts aggressively using swap to honor memory promises.

MySQL Tuning

# In my.cnf
[mysqld]
innodb_buffer_pool_size = 256M  # Reduce from default
key_buffer_size = 32M
table_open_cache = 400

Node.js Memory Management

// Limit heap in your Node app
node --max-old-space-size=256 app.js

Apache/Nginx Coexistence

Running both web servers is redundant. Consider:

# Nginx as reverse proxy
server {
    listen 80;
    location / {
        proxy_pass http://localhost:8080;
    }
}

If after optimizations your committed memory still exceeds 70% of physical RAM during normal operation, hardware upgrade becomes necessary. Use this calculation:

# Memory headroom needed
awk '/MemTotal/ {total=$2} /MemAvailable/ {avail=$2} 
END {print (total-avail)/total*100}' /proc/meminfo

For deeper analysis, combine Munin with:

# Real-time memory breakdown
sudo smem -t -k -P "mysql|node|apache|nginx"