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!
Complete Bash Exercises with Solutions
Practice Bash scripting with solved exercises to improve your command-line and scripting skills.
Link: https://macronepal.com/complete-bash-exercises-with-solutions/
Complete Guide to Bash Crontab Command
Learn how to schedule and automate tasks efficiently using the Bash crontab command.
Link: https://macronepal.com/complete-guide-to-bash-crontab-command-schedule-tasks/
Complete Guide to Bash Arrays
Understand how to create, manage, and use arrays in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-arrays/
Bash Quiz: Test Your Knowledge
Test your Bash scripting knowledge with quizzes and practical questions.
Link: https://macronepal.com/bash-quiz-test-your-knowledge/
Complete Guide to Bash Functions
Learn how to write reusable Bash functions for cleaner and efficient scripts.
Link: https://macronepal.com/complete-guide-to-bash-functions/
Complete Guide to Bash Loops
Master loops in Bash for repetitive tasks and automation.
Link: https://macronepal.com/complete-guide-to-bash-loops/
Complete Guide to Bash If-Else Statements
Learn decision-making in Bash using conditional statements.
Link: https://macronepal.com/complete-guide-to-bash-ifelse-statements/
Complete Guide to Bash Operators
Explore arithmetic, logical, and comparison operators in Bash.
Link: https://macronepal.com/complete-guide-to-bash-operators/
Complete Guide to Bash Data Types
Understand variables and different data types used in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-data-types/
Complete Guide to Bash Variables
Learn how to declare, assign, and use variables in Bash scripts.
Link: https://macronepal.com/complete-guide-to-bash-variables/
Complete Guide to Bash Scripting
A complete introduction to writing Bash scripts for automation.
Link: https://macronepal.com/complete-guide-to-bash-scripting/
Complete Guide to Basic Bash Syntax
Learn the fundamental syntax needed to write Bash commands and scripts.
Link: https://macronepal.com/complete-guide-to-basic-bash-syntax/
Complete Guide to Bash Chgrp Command
Learn how to change group ownership of files and directories.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership-2/
Complete Guide to Bash Chgrp Command (Alternate)
Another detailed explanation of Bash group ownership management.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership/
Complete Guide to Bash File Permissions and Ownership
Understand Linux file permissions and ownership management.
Link: https://macronepal.com/complete-guide-to-bash-file-permissions-and-ownership/
Complete Guide to Bash Chmod Command
Learn how to modify file permissions using chmod.
Link: https://macronepal.com/complete-guide-to-bash-chmod-command-change-file-permissions/
Complete Guide to Bash Chown Command
Change file ownership using the Bash chown command.
Link: https://macronepal.com/complete-guide-to-bash-chown-command-change-file-ownership/
Complete Guide to Bash SCP Command
Securely transfer files between systems using SCP.
Link: https://macronepal.com/complete-guide-to-bash-scp-command-secure-copy/
Complete Guide to Bash SSH Command
Learn secure remote access and server management using SSH.
Link: https://macronepal.com/complete-guide-to-bash-ssh-command/
Bash Wget Command Complete Guide
Download files from the internet using the wget command.
Link: https://macronepal.com/bash-wget-command-complete-guide-to-non-interactive-network-downloader/
