Introduction to free Command
The free command is a essential tool for monitoring system memory usage in Unix/Linux systems. It displays the total amount of free and used physical memory and swap space in the system, along with buffers and caches used by the kernel. Understanding memory usage is crucial for system administration, performance tuning, and troubleshooting.
Key Concepts
- Physical Memory (RAM): Actual system memory
- Swap Space: Disk space used as virtual memory
- Buffers: Memory used by kernel buffers
- Cache: Page cache for filesystem
- Shared Memory: Memory shared between processes
- Available Memory: Estimate of available memory for new applications
1. Basic free Syntax
Command Structure
# Basic syntax free [OPTIONS] # Common options free -h # Human-readable format free -m # Show in megabytes free -g # Show in gigabytes free -t # Show totals line free -s 5 # Repeat every 5 seconds free -c 3 # Repeat 3 times free -w # Wide mode (show more columns)
Simple Examples
# Basic memory display (in kibibytes) free # total used free shared buff/cache available # Mem: 8192000 2100000 4092000 250000 2000000 5650000 # Swap: 2097152 0 2097152 # Human-readable format (most common) free -h # total used free shared buff/cache available # Mem: 7.8G 2.0G 3.9G 238M 1.9G 5.4G # Swap: 2.0G 0B 2.0G # Show in megabytes free -m # total used free shared buff/cache available # Mem: 8000 2049 3999 244 1952 5518 # Swap: 2047 0 2047
2. Understanding free Output
Column Explanations
# Run free with explanation free -h # Columns: # total - Total installed memory # used - Memory currently in use # free - Memory not in use # shared - Memory shared between processes (tmpfs) # buff/cache - Memory used by kernel buffers and page cache # available - Estimated memory available for starting new applications # The relationship # total = used + free + buff/cache # available = free + (part of buff/cache that can be reclaimed) # Get detailed explanation free -h -t # Includes "Total" row summing physical + swap
Understanding Memory Calculations
# Memory accounting free -h # Example output analysis: # If Mem: total=8G, used=2G, free=4G, buff/cache=2G, available=5.4G # # - "used" (2G) includes memory actively used by processes # - "buff/cache" (2G) includes filesystem cache (can be reclaimed) # - "available" (5.4G) = free (4G) + reclaimable cache (1.4G) # - Actually used memory = used - (buff/cache - reclaimable) # Check what's using memory sudo slabtop # Kernel slab cache info cat /proc/meminfo # Detailed memory info vmstat -s # Memory statistics
3. Display Options
Unit Control
# Various units free -b # Bytes free -k # Kibibytes (default) free -m # Mebibytes free -g # Gibibytes free --tera # Tebibytes free --peta # Pebibytes # Examples free -m # Show in megabytes # total used free shared buff/cache available # Mem: 8000 2049 3999 244 1952 5518 # Swap: 2047 0 2047 free -g # Show in gigabytes # total used free shared buff/cache available # Mem: 7 2 3 0 1 5 # Swap: 1 0 1 # Human-readable with specific unit free -h --si # Use powers of 1000 (instead of 1024) # total used free shared buff/cache available # Mem: 8.4G 2.2G 4.2G 250M 2.0G 5.7G
Totals and Summary
# Show totals (physical + swap)
free -t
# total used free shared buff/cache available
# Mem: 8192000 2100000 4092000 250000 2000000 5650000
# Swap: 2097152 0 2097152
# Total: 10289152 2100000 6189152
# With human-readable
free -th
# total used free shared buff/cache available
# Mem: 7.8G 2.0G 3.9G 238M 1.9G 5.4G
# Swap: 2.0G 0B 2.0G
# Total: 9.8G 2.0G 5.9G
# Low memory warning
if [ $(free | awk '/^Mem:/ {print $7/$2 * 100.0}' | cut -d. -f1) -lt 20 ]; then
echo "Low memory warning!"
fi
Wide Mode
# Wide mode shows additional columns free -w # total used free shared buffers cache available # Mem: 8192000 2100000 4092000 250000 500000 1500000 5650000 # Swap: 2097152 0 2097152 # With human-readable free -wh # total used free shared buffers cache available # Mem: 7.8G 2.0G 3.9G 238M 477M 1.4G 5.4G # Swap: 2.0G 0B 2.0G # Wide mode separates buffers and cache # buffers - Memory used by kernel buffers # cache - Memory used by page cache
4. Continuous Monitoring
Periodic Updates
# Update every 5 seconds free -s 5 # (updates continuously until Ctrl+C) # Update 3 times with 2 second interval free -s 2 -c 3 # With human-readable format free -hs 5 # Save to log file free -s 60 -c 60 | while read line; do echo "$(date): $line" >> memory.log done
Real-time Monitoring
# Create memory monitor
watch -n 2 free -h
# Color-coded watch
watch -d -n 2 free -h
# Custom watch with header
watch -n 5 'free -h; echo; echo "Memory pressure:"; cat /proc/pressure/memory'
# Monitor with notifications
while true; do
available=$(free | awk '/^Mem:/ {print $7}')
total=$(free | awk '/^Mem:/ {print $2}')
pct_free=$((available * 100 / total))
if [ $pct_free -lt 10 ]; then
notify-send "Memory Alert" "Only ${pct_free}% memory available"
fi
sleep 30
done
5. Script Examples
Memory Monitor Script
#!/bin/bash # Memory monitoring script MEMORY_LOG="/var/log/memory_monitor.log" ALERT_EMAIL="[email protected]" THRESHOLD=90 # Alert when memory usage exceeds 90% monitor_memory() { while true; do # Get memory stats mem_total=$(free | awk '/^Mem:/ {print $2}') mem_used=$(free | awk '/^Mem:/ {print $3}') mem_free=$(free | awk '/^Mem:/ {print $4}') mem_available=$(free | awk '/^Mem:/ {print $7}') mem_cache=$(free | awk '/^Mem:/ {print $6}') swap_total=$(free | awk '/^Swap:/ {print $2}') swap_used=$(free | awk '/^Swap:/ {print $3}') # Calculate percentages used_percent=$((mem_used * 100 / mem_total)) available_percent=$((mem_available * 100 / mem_total)) # Get timestamp timestamp=$(date '+%Y-%m-%d %H:%M:%S') # Log memory info echo "$timestamp: Total: $mem_total KB, Used: $mem_used KB ($used_percent%), Available: $mem_available KB ($available_percent%)" >> "$MEMORY_LOG" # Check threshold if [ $used_percent -gt $THRESHOLD ]; then echo "$timestamp: CRITICAL - Memory usage at ${used_percent}%" >> "$MEMORY_LOG" # Send alert { echo "Memory Alert - $(date)" echo "Memory usage has exceeded ${THRESHOLD}%" echo free -h echo echo "Top memory processes:" ps aux --sort=-%mem | head -10 } | mail -s "Memory Alert on $(hostname)" "$ALERT_EMAIL" fi sleep 60 done } # Function to generate memory report generate_report() { { echo "=== Memory Report for $(hostname) ===" echo "Generated: $(date)" echo echo "Current Memory Status:" free -h echo echo "Memory Usage by Process:" ps aux --sort=-%mem | head -20 echo echo "VM Statistics:" vmstat 1 5 echo echo "Kernel Memory Info:" sudo slabtop -o | head -20 } > "memory_report_$(date +%Y%m%d).txt" } # Usage case "$1" in monitor) monitor_memory ;; report) generate_report ;; *) echo "Usage: $0 {monitor|report}" exit 1 ;; esac
Memory Alert System
#!/bin/bash
# Advanced memory alert system
CONFIG_FILE="/etc/memory_alert.conf"
ALERT_LOG="/var/log/memory_alert.log"
# Default values
MEM_THRESHOLD=90
SWAP_THRESHOLD=50
CHECK_INTERVAL=30
ALERT_COMMANDS=()
# Load configuration
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
check_memory() {
local mem_used=$(free | awk '/^Mem:/ {printf "%.0f", ($3/$2)*100}')
local mem_available=$(free | awk '/^Mem:/ {printf "%.0f", ($7/$2)*100}')
local swap_used=$(free | awk '/^Swap:/ {if($2>0) printf "%.0f", ($3/$2)*100; else echo "0"}')
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# Check memory
if [ $mem_used -gt $MEM_THRESHOLD ]; then
log_alert "MEMORY" "$mem_used" "$MEM_THRESHOLD"
execute_alerts "memory" "$mem_used"
fi
# Check swap
if [ $swap_used -gt $SWAP_THRESHOLD ]; then
log_alert "SWAP" "$swap_used" "$SWAP_THRESHOLD"
execute_alerts "swap" "$swap_used"
fi
# Log current status
echo "$timestamp: MEM=${mem_used}% SWAP=${swap_used}% AVAIL=${mem_available}%" >> "$ALERT_LOG"
}
log_alert() {
local type=$1
local value=$2
local threshold=$3
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "$timestamp: ALERT - $type usage at ${value}% (threshold: ${threshold}%)" >> "$ALERT_LOG"
}
execute_alerts() {
local type=$1
local value=$2
for cmd in "${ALERT_COMMANDS[@]}"; do
eval "$cmd" "$type" "$value"
done
}
# Example alert commands
ALERT_COMMANDS=(
'notify-send "Memory Alert" "Memory usage: $2%"'
'logger -t memory-alert "Memory usage: $2%"'
'echo "$(date): $1 usage at $2%" >> /var/log/memory_critical.log'
)
# Continuous monitoring
while true; do
check_memory
sleep $CHECK_INTERVAL
done
6. Integration with Other Commands
With ps for Process Memory
# Compare with process memory
free -h
echo "Top memory processes:"
ps aux --sort=-%mem | head -5
# Find which process is using most memory
ps aux --sort=-%mem | head -2 | tail -1
# Total memory used by processes vs free
ps_mem=$(ps aux --no-headers | awk '{sum+=$6} END {print sum}')
free_mem=$(free | awk '/^Mem:/ {print $4}')
echo "Processes using: $(numfmt --to=iec $((ps_mem*1024)))"
echo "Free memory: $(numfmt --to=iec $((free_mem*1024)))"
# Check if process memory + free < total (indicates cache)
total=$(free | awk '/^Mem:/ {print $2}')
used_by_ps=$(ps aux --no-headers | awk '{sum+=$6} END {print sum}')
free_actual=$(free | awk '/^Mem:/ {print $4}')
cache=$((total - used_by_ps - free_actual))
echo "Memory in cache: $(numfmt --to=iec $((cache*1024)))"
With vmstat for Detailed Analysis
# Combine free with vmstat
free -h
vmstat 1 3
# Memory stats from vmstat
vmstat -s | grep -E "total memory|used memory|free memory"
# Monitor memory and swap activity
while true; do
clear
free -h
echo
vmstat -S M 1 2 | tail -1 | awk '{print "Swap in/out: " $7 "/" $8 " MB/s"}'
sleep 5
done
With top/htop
# Compare with top summary free -h top -n 1 -b | head -4 # Memory summary from top top -n 1 -b | grep "MiB Mem" # Use with htop for interactive view htop # Press F6 to sort by memory percentage
With awk for Calculations
# Calculate percentages
free | awk '
/^Mem:/ {
printf "Memory Usage: %.1f%%\n", ($3/$2)*100
printf "Memory Available: %.1f%%\n", ($7/$2)*100
}
/^Swap:/ && $2 > 0 {
printf "Swap Usage: %.1f%%\n", ($3/$2)*100
}
'
# Track memory over time
free -s 5 | awk '
/^Mem:/ {
gsub(/[^0-9]/, "", $1)
if (++count % 10 == 0) {
printf "[%s] Mem: %dM used, %dM free\n",
strftime("%H:%M:%S"), $3/1024, $4/1024
}
}
'
# Memory trend analysis
free -s 60 -c 60 | awk '
/^Mem:/ && NR>1 {
used[++i] = $3
if (i > 1) {
trend = used[i] - used[i-1]
if (trend > 0) print strftime("%H:%M"), "Memory increasing by", trend/1024, "MB"
}
}
'
7. Performance Analysis
Memory Pressure Detection
#!/bin/bash
# Memory pressure analysis
analyze_memory_pressure() {
echo "=== Memory Pressure Analysis ==="
echo "Time: $(date)"
echo
# Basic memory info
free -h
# Memory pressure from /proc
if [ -f /proc/pressure/memory ]; then
echo -e "\nMemory Pressure Stall Information:"
cat /proc/pressure/memory
fi
# Swap usage
swap_total=$(free | awk '/^Swap:/ {print $2}')
if [ $swap_total -gt 0 ]; then
swap_used=$(free | awk '/^Swap:/ {print $3}')
swap_percent=$((swap_used * 100 / swap_total))
if [ $swap_percent -gt 0 ]; then
echo -e "\n⚠️ Swap is being used (${swap_percent}%)"
if [ $swap_percent -gt 50 ]; then
echo "🔴 High swap usage - system may be thrashing"
fi
fi
fi
# Check for OOM (Out of Memory) conditions
if dmesg | grep -q "Out of memory"; then
echo -e "\n🔴 Out of Memory events detected in kernel log"
dmesg | grep "Out of memory" | tail -5
fi
# Memory fragmentation
echo -e "\nMemory fragmentation (higher is worse):"
grep -E "^(Dirty|Writeback|Mapped|PageTables)" /proc/meminfo
}
# Memory leak detection
detect_memory_leak() {
local pid=$1
local samples=10
local interval=5
echo "Monitoring process $pid for memory leaks..."
echo "Sample every ${interval}s for ${samples} samples"
for i in $(seq 1 $samples); do
rss=$(ps -o rss= -p $pid 2>/dev/null)
if [ -z "$rss" ]; then
echo "Process $pid not found"
return 1
fi
echo "$(date +%H:%M:%S): RSS = $(numfmt --to=iec $((rss*1024)))"
sleep $interval
done
}
# Usage
analyze_memory_pressure
detect_memory_leak 1234
Cache Analysis
#!/bin/bash
# Analyze cache usage
analyze_cache() {
echo "=== Cache Analysis ==="
# Get cache information
mem_info=$(cat /proc/meminfo)
cached=$(echo "$mem_info" | grep "^Cached:" | awk '{print $2}')
buffers=$(echo "$mem_info" | grep "^Buffers:" | awk '{print $2}')
swap_cached=$(echo "$mem_info" | grep "^SwapCached:" | awk '{print $2}')
dirty=$(echo "$mem_info" | grep "^Dirty:" | awk '{print $2}')
writeback=$(echo "$mem_info" | grep "^Writeback:" | awk '{print $2}')
total_cache=$((cached + buffers))
total_mem=$(free | awk '/^Mem:/ {print $2}')
cache_percent=$((total_cache * 100 / total_mem))
echo "Cache Statistics:"
echo " Page Cache: $(numfmt --to=iec $((cached*1024)))"
echo " Buffers: $(numfmt --to=iec $((buffers*1024)))"
echo " Total Cache: $(numfmt --to=iec $((total_cache*1024))) ($cache_percent% of RAM)"
echo
echo "Cache State:"
echo " Dirty: $(numfmt --to=iec $((dirty*1024)))"
echo " Writeback: $(numfmt --to=iec $((writeback*1024)))"
if [ $dirty -gt 100000 ]; then # >100MB dirty
echo "⚠️ Large amount of dirty cache - I/O bottleneck possible"
fi
# Clear cache suggestion
if [ $cache_percent -gt 80 ]; then
echo
echo "💡 To clear cache (if needed):"
echo " sync; echo 3 > /proc/sys/vm/drop_caches"
fi
}
# Monitor cache changes
watch_cache() {
while true; do
clear
echo "Cache Monitor - $(date)"
echo "────────────────────────────"
cached=$(grep "^Cached:" /proc/meminfo | awk '{print $2}')
buffers=$(grep "^Buffers:" /proc/meminfo | awk '{print $2}')
dirty=$(grep "^Dirty:" /proc/meminfo | awk '{print $2}')
echo "Cached: $(numfmt --to=iec $((cached*1024)))"
echo "Buffers: $(numfmt --to=iec $((buffers*1024)))"
echo "Dirty: $(numfmt --to=iec $((dirty*1024)))"
# Simple bar graph
total=$((cached + buffers))
if [ $total -gt 0 ]; then
bars=$((total / 1024 / 1024)) # Scale to MB for bars
[ $bars -gt 50 ] && bars=50
printf "Cache: [%-50s]\n" "$(printf '#%.0s' $(seq 1 $((bars))))"
fi
sleep 2
done
}
8. Historical Data Collection
Memory Logging
#!/bin/bash
# Memory history logger
MEMORY_HISTORY_DIR="/var/log/memory_history"
mkdir -p "$MEMORY_HISTORY_DIR"
log_memory_snapshot() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local logfile="$MEMORY_HISTORY_DIR/memory_$timestamp.log"
{
echo "=== Memory Snapshot: $(date) ==="
echo
free -h
echo
echo "=== Detailed Memory Info ==="
cat /proc/meminfo
echo
echo "=== Top Memory Processes ==="
ps aux --sort=-%mem | head -20
echo
echo "=== VM Statistics ==="
vmstat -s
} > "$logfile"
# Keep last 24 hours of logs (assuming one per hour)
find "$MEMORY_HISTORY_DIR" -name "memory_*.log" -mtime +1 -delete
}
# Generate daily report
generate_daily_report() {
local date=${1:-$(date +%Y%m%d)}
local report_file="$MEMORY_HISTORY_DIR/report_$date.txt"
{
echo "=== Daily Memory Report for $date ==="
echo
# Find logs for this date
for log in "$MEMORY_HISTORY_DIR"/memory_${date}_*.log; do
[ -f "$log" ] || continue
echo "--- $(basename "$log" .log | cut -d_ -f2) ---"
grep -E "^(Mem|Swap):" "$log"
echo
done
# Calculate averages
echo "=== Averages ==="
awk '
/^Mem:/ {
total_mem=$2; used+=$3; free+=$4; available+=$7; count++
}
END {
if (count > 0) {
printf "Average Memory Usage: %.1f%%\n", (used/count)/(total_mem/count)*100
printf "Average Available: %.1f MB\n", (available/count)/1024
}
}
' "$MEMORY_HISTORY_DIR"/memory_${date}_*.log
} > "$report_file"
echo "Report generated: $report_file"
}
# Usage
log_memory_snapshot
generate_daily_report
Trend Analysis
#!/bin/bash
# Memory trend analysis
analyze_trends() {
local days=${1:-7}
local log_dir="/var/log/memory_history"
echo "=== Memory Trend Analysis (Last $days Days) ==="
echo
# Collect daily averages
for i in $(seq 0 $((days-1))); do
date=$(date -d "-$i days" +%Y%m%d)
pattern="$log_dir/memory_${date}_*.log"
if ls $pattern 2>/dev/null; then
avg_used=$(awk '/^Mem:/ {used+=$3; count++} END {if(count>0) print used/count}' $pattern)
avg_available=$(awk '/^Mem:/ {avail+=$7; count++} END {if(count>0) print avail/count}' $pattern)
if [ -n "$avg_used" ]; then
printf "%s: Used: %'d KB, Available: %'d KB\n" "$date" "${avg_used%.*}" "${avg_available%.*}"
fi
fi
done
# Detect trends
echo -e "\nTrend Analysis:"
# Check if memory usage is increasing
last_values=$(grep -h "^Mem:" "$log_dir"/memory_*.log | tail -5 | awk '{print $3}')
if [ -n "$last_values" ]; then
first=$(echo "$last_values" | head -1)
last=$(echo "$last_values" | tail -1)
if [ $last -gt $first ]; then
echo "⚠️ Memory usage appears to be increasing"
increase=$((last - first))
echo " Increase: $(numfmt --to=iec $((increase*1024))) over last 5 samples"
fi
fi
}
# Usage
analyze_trends 7
9. Integration with Monitoring Systems
Prometheus Format
#!/bin/bash
# Export memory metrics for Prometheus
export_prometheus() {
local timestamp=$(date +%s)
# Get memory stats
eval $(free | awk '
/^Mem:/ {
printf "mem_total=%d;mem_used=%d;mem_free=%d;mem_shared=%d;mem_buffcache=%d;mem_available=%d;",
$2, $3, $4, $5, $6, $7
}
/^Swap:/ {
printf "swap_total=%d;swap_used=%d;swap_free=%d", $2, $3, $4
}
')
# Output in Prometheus format
cat << EOF
# HELP node_memory_MemTotal_bytes Memory information
# TYPE node_memory_MemTotal_bytes gauge
node_memory_MemTotal_bytes $((mem_total * 1024))
# HELP node_memory_MemFree_bytes Memory information
node_memory_MemFree_bytes $((mem_free * 1024))
# HELP node_memory_MemAvailable_bytes Memory information
node_memory_MemAvailable_bytes $((mem_available * 1024))
# HELP node_memory_Buffers_bytes Memory information
node_memory_Buffers_bytes $((buffers * 1024))
# HELP node_memory_Cached_bytes Memory information
node_memory_Cached_bytes $((cache * 1024))
# HELP node_memory_SwapTotal_bytes Swap information
node_memory_SwapTotal_bytes $((swap_total * 1024))
# HELP node_memory_SwapFree_bytes Swap information
node_memory_SwapFree_bytes $((swap_free * 1024))
EOF
}
# Usage in Prometheus node_exporter textfile collector
export_prometheus > /var/lib/node_exporter/textfile/memory.prom
Nagios/Icinga Check
#!/bin/bash
# Nagios plugin for memory check
STATE_OK=0
STATE_WARNING=1
STATE_CRITICAL=2
STATE_UNKNOWN=3
# Default thresholds
WARN=80
CRIT=90
# Parse arguments
while getopts "w:c:" opt; do
case $opt in
w) WARN=$OPTARG ;;
c) CRIT=$OPTARG ;;
*) echo "Usage: $0 [-w warn_percent] [-c crit_percent]"
exit $STATE_UNKNOWN ;;
esac
done
# Get memory usage
mem_used_percent=$(free | awk '/^Mem:/ {printf "%.0f", ($3/$2)*100}')
mem_available=$(free -h | awk '/^Mem:/ {print $7}')
# Check thresholds
if [ $mem_used_percent -ge $CRIT ]; then
echo "CRITICAL - Memory usage at ${mem_used_percent}% (available: $mem_available)"
exit $STATE_CRITICAL
elif [ $mem_used_percent -ge $WARN ]; then
echo "WARNING - Memory usage at ${mem_used_percent}% (available: $mem_available)"
exit $STATE_WARNING
else
echo "OK - Memory usage at ${mem_used_percent}% (available: $mem_available)"
exit $STATE_OK
fi
Grafana Integration
#!/bin/bash
# Collect metrics for Grafana
while true; do
timestamp=$(date +%s%N)
# Get metrics
mem_total=$(free | awk '/^Mem:/ {print $2}')
mem_used=$(free | awk '/^Mem:/ {print $3}')
mem_free=$(free | awk '/^Mem:/ {print $4}')
mem_available=$(free | awk '/^Mem:/ {print $7}')
mem_cache=$(free | awk '/^Mem:/ {print $6}')
# Send to InfluxDB or similar
curl -i -XPOST "http://influxdb:8086/write?db=metrics" \
--data-binary "memory,host=$(hostname) total=$mem_total,used=$mem_used,free=$mem_free,available=$mem_available,cache=$mem_cache $timestamp"
sleep 60
done
10. Advanced Memory Analysis
Slab Cache Analysis
#!/bin/bash
# Analyze kernel slab cache
analyze_slab() {
echo "=== Kernel Slab Cache Analysis ==="
echo
# Show top slab caches by memory usage
sudo slabtop -o --once | head -20
# Detailed slab info
echo -e "\nSlab Memory Totals:"
grep -E "^(SReclaimable|SUnreclaim)" /proc/meminfo
# Check for memory fragmentation
echo -e "\nMemory fragmentation (higher values indicate more fragmentation):"
grep -E "^(Active|Inactive)" /proc/meminfo
# HugePages info
echo -e "\nHugePages Information:"
grep -E "^(HugePages|Hugetlb)" /proc/meminfo
}
# Monitor slab changes
watch_slab() {
while true; do
clear
echo "Slab Monitor - $(date)"
echo "────────────────────────────"
sudo slabtop -o --once | head -15
sleep 2
done
}
NUMA Memory Analysis
#!/bin/bash
# NUMA memory analysis (if applicable)
if command -v numactl &> /dev/null; then
analyze_numa() {
echo "=== NUMA Memory Analysis ==="
echo
# Show NUMA hardware
numactl --hardware
echo -e "\nNUMA Memory Stats:"
numastat
echo -e "\nProcess NUMA policy:"
numactl --show
}
# Show memory per NUMA node
numa_memory() {
echo "Memory per NUMA node:"
for node in /sys/devices/system/node/node*; do
if [ -d "$node" ]; then
node_num=$(basename "$node" | sed 's/node//')
meminfo="$node/meminfo"
if [ -f "$meminfo" ]; then
total=$(grep "MemTotal" "$meminfo" | awk '{print $4}')
free=$(grep "MemFree" "$meminfo" | awk '{print $4}')
echo "Node $node_num: Total: $total KB, Free: $free KB"
fi
fi
done
}
fi
# Usage
analyze_numa
numa_memory
11. Troubleshooting Memory Issues
Common Problems and Solutions
#!/bin/bash
# Memory troubleshooting script
troubleshoot_memory() {
echo "=== Memory Troubleshooting ==="
echo
# Check overall memory
echo "1. Current Memory Status:"
free -h
echo
# Check for high memory usage
mem_used_percent=$(free | awk '/^Mem:/ {printf "%.0f", ($3/$2)*100}')
if [ $mem_used_percent -gt 90 ]; then
echo "⚠️ CRITICAL: Memory usage at ${mem_used_percent}%"
echo
echo "2. Top memory consumers:"
ps aux --sort=-%mem | head -10
echo
echo "3. Checking for memory leaks..."
echo " Look for processes with increasing RSS over time"
fi
# Check swap usage
swap_used=$(free | awk '/^Swap:/ {print $3}')
if [ $swap_used -gt 0 ]; then
echo
echo "4. Swap is being used ($(numfmt --to=iec $((swap_used*1024))))"
echo " This may indicate memory pressure"
vmstat 1 5
fi
# Check for OOM killer
if dmesg | grep -q "Out of memory"; then
echo
echo "5. Out of Memory events detected:"
dmesg | grep "Out of memory" | tail -5
fi
# Check cache usage
cache=$(free | awk '/^Mem:/ {print $6}')
total=$(free | awk '/^Mem:/ {print $2}')
cache_percent=$((cache * 100 / total))
if [ $cache_percent -gt 50 ]; then
echo
echo "6. Cache is using ${cache_percent}% of memory"
echo " This is normal - cache improves performance"
fi
# Recommendations
echo
echo "=== Recommendations ==="
if [ $mem_used_percent -gt 80 ]; then
echo "- Consider adding more RAM"
echo "- Check for memory leaks in applications"
echo "- Review running services and disable unnecessary ones"
echo "- Adjust kernel parameters (vm.swappiness, vm.vfs_cache_pressure)"
fi
if [ $swap_used -gt 0 ]; then
echo "- System is swapping - check I/O performance"
echo "- Consider increasing RAM if swap usage is persistent"
fi
}
# Quick memory check
quick_check() {
echo "=== Quick Memory Check ==="
free -h | head -2
echo
ps aux --sort=-%mem | head -6
}
# Usage
troubleshoot_memory
quick_check
Memory Leak Detection
#!/bin/bash
# Memory leak detection script
DETECTION_LOG="/var/log/memory_leak.log"
monitor_process_memory() {
local pid=$1
local duration=${2:-300} # 5 minutes default
local interval=10
local samples=$((duration / interval))
local log_file="memory_${pid}_$(date +%Y%m%d).log"
echo "Monitoring process $pid for $duration seconds"
echo "Logging to $log_file"
{
echo "=== Memory Leak Detection for PID $pid ==="
echo "Started: $(date)"
echo "Duration: ${duration}s, Interval: ${interval}s"
echo
echo "Timestamp, RSS(KB), VSZ(KB), %MEM, Command"
} > "$log_file"
for i in $(seq 1 $samples); do
if ! ps -p "$pid" > /dev/null 2>&1; then
echo "Process $pid terminated at $(date)" >> "$log_file"
break
fi
timestamp=$(date +%H:%M:%S)
stats=$(ps -p "$pid" -o rss=,vsz=,pmem=,comm= --no-headers 2>/dev/null)
if [ -n "$stats" ]; then
echo "$timestamp, $stats" | tr -s ' ' ',' >> "$log_file"
# Check for significant growth
if [ $i -gt 1 ]; then
prev_rss=$(tail -2 "$log_file" | head -1 | cut -d, -f2)
curr_rss=$(echo "$stats" | awk '{print $1}')
if [ $curr_rss -gt $((prev_rss * 2)) ] && [ $curr_rss -gt 100000 ]; then
echo "⚠️ WARNING: Memory doubled in last ${interval}s!" >> "$log_file"
fi
fi
fi
sleep $interval
done
# Analyze results
echo
echo "=== Analysis ==="
awk -F, '
NR>2 {
rss[++n] = $2
}
END {
if (n > 1) {
start = rss[1]
end = rss[n]
growth = end - start
if (growth > 0) {
printf "Memory growth: +%d KB (%.1f%%)\n", growth, (growth/start)*100
if (growth > start) {
print "⚠️ Possible memory leak detected!"
}
} else {
print "No significant memory growth detected"
}
}
}
' "$log_file"
}
# Usage
monitor_process_memory 1234 600
12. System Tuning Based on free Output
Memory Tuning Script
#!/bin/bash
# Memory tuning based on free output
tune_memory() {
echo "=== Memory Tuning ==="
# Get current stats
mem_total=$(free | awk '/^Mem:/ {print $2}')
mem_available=$(free | awk '/^Mem:/ {print $7}')
mem_used_percent=$(( (mem_total - mem_available) * 100 / mem_total ))
# Get current sysctl values
swappiness=$(sysctl -n vm.swappiness)
cache_pressure=$(sysctl -n vm.vfs_cache_pressure)
echo "Current settings:"
echo " Memory usage: ${mem_used_percent}%"
echo " Swappiness: $swappiness"
echo " Cache pressure: $cache_pressure"
echo
# Suggest optimizations
echo "Suggestions:"
# Swappiness tuning
if [ $mem_used_percent -gt 90 ]; then
echo "- Increase swappiness to move less-used data to swap"
echo " sysctl -w vm.swappiness=$((swappiness + 10))"
elif [ $mem_used_percent -lt 50 ]; then
echo "- Decrease swappiness to keep data in RAM"
echo " sysctl -w vm.swappiness=$((swappiness - 10))"
fi
# Cache pressure tuning
if [ $mem_used_percent -gt 80 ]; then
echo "- Increase cache pressure to reclaim cache more aggressively"
echo " sysctl -w vm.vfs_cache_pressure=$((cache_pressure + 50))"
fi
# Transparent Huge Pages (THP)
if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; then
thp=$(cat /sys/kernel/mm/transparent_hugepage/enabled | grep -o '\[.*\]')
echo "- THP is currently $thp"
echo " Consider 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' for some workloads"
fi
# Dirty page ratios
dirty_ratio=$(sysctl -n vm.dirty_ratio)
dirty_bg_ratio=$(sysctl -n vm.dirty_background_ratio)
echo
echo "Dirty page settings:"
echo " vm.dirty_ratio = $dirty_ratio"
echo " vm.dirty_background_ratio = $dirty_bg_ratio"
}
# Apply tuning
apply_tuning() {
local new_swappiness=${1:-60}
local new_cache_pressure=${2:-200}
echo "Applying memory tuning..."
sudo sysctl -w vm.swappiness=$new_swappiness
sudo sysctl -w vm.vfs_cache_pressure=$new_cache_pressure
# Make permanent
cat << EOF | sudo tee -a /etc/sysctl.conf
# Memory tuning added $(date)
vm.swappiness=$new_swappiness
vm.vfs_cache_pressure=$new_cache_pressure
EOF
echo "Tuning applied and added to /etc/sysctl.conf"
}
# Usage
tune_memory
# apply_tuning 30 150
13. GUI and Visualization
ASCII Memory Graph
#!/bin/bash
# Create ASCII memory usage graph
memory_graph() {
local width=${1:-50}
local interval=${2:-2}
while true; do
clear
echo "Memory Usage - $(date)"
echo
# Get memory stats
mem_used_percent=$(free | awk '/^Mem:/ {printf "%.0f", ($3/$2)*100}')
mem_available_percent=$(free | awk '/^Mem:/ {printf "%.0f", ($7/$2)*100}')
swap_used=$(free | awk '/^Swap:/ {if($2>0) printf "%.0f", ($3/$2)*100; else print "0"}')
# Draw memory bar
bars=$((mem_used_percent * width / 100))
echo -n "Memory ["
for ((i=0; i<width; i++)); do
if [ $i -lt $bars ]; then
if [ $mem_used_percent -gt 80 ]; then
echo -n "█" # Full block for high usage
elif [ $mem_used_percent -gt 50 ]; then
echo -n "▓" # Medium block
else
echo -n "▒" # Light block
fi
else
echo -n "░" # Empty
fi
done
echo "] ${mem_used_percent}%"
# Draw available memory
avail_bars=$((mem_available_percent * width / 100))
echo -n "Available ["
for ((i=0; i<width; i++)); do
if [ $i -lt $avail_bars ]; then
echo -n "▒"
else
echo -n "░"
fi
done
echo "] ${mem_available_percent}%"
# Draw swap if present
if [ $swap_used -gt 0 ]; then
swap_bars=$((swap_used * width / 100))
echo -n "Swap ["
for ((i=0; i<width; i++)); do
if [ $i -lt $swap_bars ]; then
echo -n "▒"
else
echo -n "░"
fi
done
echo "] ${swap_used}%"
fi
# Show numbers
echo
free -h | head -2
echo
echo "Press Ctrl+C to exit"
sleep $interval
done
}
# Simple memory meter
memory_meter() {
while true; do
mem=$(free | awk '/^Mem:/ {printf "%.1f", (($3-$6-$7)/$2)*100}')
bars=$(( ${mem%.*} / 2 ))
printf "\rMemory: [%-50s] %5.1f%%" "$(printf '#%.0s' $(seq 1 $bars))" "$mem"
sleep 1
done
}
# Usage
memory_graph 60 1
# memory_meter
Terminal UI with Dialog
#!/bin/bash
# Interactive memory monitor with dialog
if command -v dialog &> /dev/null; then
interactive_monitor() {
while true; do
# Get memory data
mem_total=$(free -h | awk '/^Mem:/ {print $2}')
mem_used=$(free -h | awk '/^Mem:/ {print $3}')
mem_free=$(free -h | awk '/^Mem:/ {print $4}')
mem_shared=$(free -h | awk '/^Mem:/ {print $5}')
mem_cache=$(free -h | awk '/^Mem:/ {print $6}')
mem_avail=$(free -h | awk '/^Mem:/ {print $7}')
swap_total=$(free -h | awk '/^Swap:/ {print $2}')
swap_used=$(free -h | awk '/^Swap:/ {print $3}')
swap_free=$(free -h | awk '/^Swap:/ {print $4}')
# Create gauge display
mem_used_percent=$(free | awk '/^Mem:/ {printf "%.0f", ($3/$2)*100}')
{
echo "$mem_used_percent"
echo "XXX"
echo "Memory Usage: ${mem_used_percent}%"
echo "Total: $mem_total"
echo "Used: $mem_used"
echo "Free: $mem_free"
echo "Cache: $mem_cache"
echo "Avail: $mem_avail"
echo "Swap: $swap_used/$swap_total"
echo "XXX"
} | dialog --title "Memory Monitor" --gauge "Memory Status" 12 60 0
# Check for exit
read -t 2 -n 1 key
if [ "$key" = "q" ]; then
break
fi
done
}
fi
# Usage
interactive_monitor
14. Container Memory Monitoring
Docker Container Memory
#!/bin/bash
# Monitor Docker container memory
if command -v docker &> /dev/null; then
docker_memory() {
echo "=== Docker Container Memory ==="
echo
# List containers with memory stats
docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}"
echo
echo "Detailed per container:"
for container in $(docker ps -q); do
name=$(docker inspect --format '{{.Name}}' "$container" | sed 's/^\///')
mem_limit=$(docker inspect --format '{{.HostConfig.Memory}}' "$container")
mem_usage=$(docker stats --no-stream --format '{{.MemUsage}}' "$container")
echo "$name: $mem_usage (Limit: $(numfmt --to=iec $mem_limit))"
done
}
# Check container memory pressure
docker_memory_pressure() {
for container in $(docker ps -q); do
name=$(docker inspect --format '{{.Name}}' "$container" | sed 's/^\///')
mem_percent=$(docker stats --no-stream --format '{{.MemPerc}}' "$container" | sed 's/%//')
if [ -n "$mem_percent" ] && [ "${mem_percent%.*}" -gt 80 ]; then
echo "⚠️ Container $name is using ${mem_percent}% memory"
fi
done
}
fi
# Usage
docker_memory
docker_memory_pressure
Kubernetes Pod Memory
#!/bin/bash
# Monitor Kubernetes pod memory
if command -v kubectl &> /dev/null; then
k8s_memory() {
echo "=== Kubernetes Pod Memory ==="
echo
# Get pod memory usage
kubectl top pods --all-namespaces
echo
echo "Memory requests/limits:"
kubectl get pods --all-namespaces -o json | jq -r '
.items[] |
.metadata.namespace as $ns |
.metadata.name as $name |
.spec.containers[] |
[$ns, $name, .name,
(.resources.requests.memory // "none"),
(.resources.limits.memory // "none")] |
@tsv
' | column -t
}
# Check for memory pressure
k8s_memory_pressure() {
kubectl top pods --all-namespaces --no-headers | while read -r line; do
namespace=$(echo "$line" | awk '{print $1}')
pod=$(echo "$line" | awk '{print $2}')
memory=$(echo "$line" | awk '{print $4}' | sed 's/Mi//')
if [ "$memory" -gt 1024 ]; then # >1GB
echo "⚠️ Pod $namespace/$pod using ${memory}Mi memory"
fi
done
}
fi
15. Best Practices and Tips
Memory Monitoring Best Practices
#!/bin/bash
# Best practices for memory monitoring
# 1. Regular monitoring
setup_cron_monitoring() {
# Add to crontab for hourly monitoring
(crontab -l 2>/dev/null; echo "0 * * * * /usr/local/bin/memory_check.sh") | crontab -
echo "Hourly memory monitoring configured"
}
# 2. Alert thresholds
setup_alerts() {
cat > /etc/memory_alert.conf << 'EOF'
# Memory alert configuration
MEM_WARN=80
MEM_CRIT=90
SWAP_WARN=20
SWAP_CRIT=50
[email protected]
EOF
echo "Alert thresholds configured"
}
# 3. Historical data collection
setup_history() {
cat > /etc/cron.d/memory-history << 'EOF'
# Collect memory stats every 5 minutes
*/5 * * * * root /usr/local/bin/collect_memory_stats.sh
EOF
echo "Historical data collection configured"
}
# 4. Performance baseline
create_baseline() {
echo "Creating memory usage baseline..."
# Collect data for 24 hours
for i in {1..24}; do
timestamp=$(date +%Y%m%d_%H%M%S)
free -h >> "/var/log/memory_baseline_$(date +%Y%m%d).log"
sleep 3600
done
echo "Baseline created in /var/log/memory_baseline_*.log"
}
# 5. Automated response
setup_auto_response() {
cat > /usr/local/bin/memory_response.sh << 'EOF'
#!/bin/bash
# Automatic response to high memory
MEM_CRIT=95
check_and_respond() {
mem_used=$(free | awk '/^Mem:/ {printf "%.0f", ($3/$2)*100}')
if [ $mem_used -gt $MEM_CRIT ]; then
echo "$(date): Memory critical - taking action"
# Restart top memory service
top_service=$(ps aux --sort=-%mem | awk 'NR==2 {print $11}')
systemctl restart "$top_service"
# Clear cache if needed
sync && echo 3 > /proc/sys/vm/drop_caches
logger -t memory-response "Automatic response triggered"
fi
}
check_and_respond
EOF
chmod +x /usr/local/bin/memory_response.sh
# Add to cron for regular checks
(crontab -l 2>/dev/null; echo "*/5 * * * * /usr/local/bin/memory_response.sh") | crontab -
}
# Usage
setup_cron_monitoring
setup_alerts
setup_history
# create_baseline
# setup_auto_response
Quick Reference Card
# Most Common free Commands free -h # Human-readable (most used) free -m # Megabytes free -s 5 # Update every 5 seconds free -t # Show total (RAM + swap) free -w # Wide mode (separate buffers/cache) # Memory Analysis free -h && ps aux --sort=-%mem | head -10 # Memory + top processes watch -n 2 free -h # Real-time monitoring vmstat -s | grep memory # Detailed memory stats cat /proc/meminfo # All memory info # Memory Calculations # used = total - free - buffers - cache # available = free + (reclaimable cache) # actual used = total - available # Critical Values # < 10% free + cache: Critical # 10-20% free + cache: Warning # > 20% free + cache: Normal
Conclusion
The free command is essential for memory monitoring in Linux systems:
Key Takeaways
- Basic Information: Total, used, free, shared, buff/cache, available memory
- Human-readable:
-hoption for easy reading - Continuous Monitoring:
-sfor periodic updates - Swap Monitoring: Check swap usage for memory pressure
- Available Memory: Most important metric for application memory
- Cache is Good: Don't confuse cache with used memory
Common Use Cases
| Task | Command |
|---|---|
| Quick memory check | free -h |
| Continuous monitoring | watch -n 2 free -h |
| Check before starting app | free -h && ps aux | sort -nrk 4 | head |
| Investigate high memory | free -h && ps aux --sort=-%mem | head -10 |
| Memory trend | free -s 60 | tee -a memory.log |
| Alert on low memory | awk '/^Mem:/ {if ($7/$2 < 0.1) print "Low memory!"}' |
Best Practices
- Always use
-hfor human-readable output - Monitor
availablenotfreememory - Set up alerts for low memory conditions
- Track memory trends over time
- Investigate high swap usage
- Understand that cache is not "used" memory
- Combine with
psto identify memory-hungry processes
The free command is your first line of defense in memory monitoring. Master it for effective system administration!