Linux Check Memory Usage: free vs top Explained
Check memory usage in Linux using free, top, and /proc/meminfo — understand available vs free, detect memory pressure, and identify memory-hungry processes.
free says you have 200MB free. Your monitoring alert says memory is critical. Both are technically correct — and that's the confusion. Here's what the numbers actually mean.
TL;DR
free -h # system memory overview
top # live, press M to sort by memory
ps aux --sort=-%mem | head -10 # which processes use most RAM
cat /proc/meminfo | grep -E "MemTotal|MemAvailable|SwapUsed"
free: The Starting Point
free -h
total used free shared buff/cache available
Mem: 15Gi 9.8Gi 213Mi 1.2Gi 5.1Gi 4.4Gi
Swap: 4.0Gi 0B 4.0Gi
The column that matters: available — not free.
free (213Mi) = RAM with nothing in it at all.
available (4.4Gi) = RAM available for new processes, including reclaimable cache.
Linux uses unused RAM as disk cache (buff/cache). That's intentional — it speeds up I/O. The kernel releases it immediately when an app needs memory. So 213Mi free with 4.4Gi available means you have plenty of memory. A server with 200Mi free and 200Mi available is actually low on memory.
Reading free Output
free -h -s 2 # update every 2 seconds (like watch)
| Column | Meaning | What to watch |
|---|---|---|
total |
Physical RAM installed | — |
used |
RAM in active use by processes | — |
free |
Completely idle RAM | Not the important number |
shared |
tmpfs and shared memory | — |
buff/cache |
Kernel buffers + page cache | Reclaimable |
available |
RAM available for new allocations | This is the important number |
Swap used > 0: System needed more RAM than available at some point. Watch if it's growing.
top: Memory in Context
top
# Press M to sort processes by memory
Memory header in top:
MiB Mem : 15258.9 total, 213.1 free, 9842.6 used, 5203.2 buff/cache
MiB Swap: 4096.0 total, 0.0 used, 4096.0 free. 4412.5 avail Mem
avail Mem on the right — same as available in free, this is the real number.
Per-process memory columns:
| Column | Meaning |
|---|---|
VIRT |
Virtual memory — includes mapped files, often misleading |
RES |
Resident Set Size — actual physical RAM used |
SHR |
Shared memory (shared libraries) |
%MEM |
RES as % of total RAM |
Always use RES for memory analysis. VIRT (or VSZ in ps) is almost always misleadingly large.
ps: Find Memory-Hungry Processes
# Sort by RSS (actual RAM), top 10
ps aux --sort=-%mem | head -11
# Custom columns: PID, RSS in MB, command
ps -eo pid,user,rss,comm --sort=-rss | head -11 | \
awk 'NR==1{print} NR>1{printf "%-8s %-12s %6.1fMB %s\n", $1, $2, $3/1024, $4}'
Output:
PID USER RSS COMMAND
8823 app 2048.3MB java
3012 postgres 512.1MB postgres
2341 www-data 48.2MB nginx
/proc/meminfo: Detailed Breakdown
cat /proc/meminfo
Key entries:
MemTotal: 15625216 kB ← total RAM
MemFree: 218112 kB ← idle RAM (not important alone)
MemAvailable: 4519424 kB ← available for new allocations
Buffers: 239104 kB ← kernel buffer cache
Cached: 4923904 kB ← page cache (reclaimable)
SwapTotal: 4194304 kB
SwapFree: 4194304 kB ← if < SwapTotal: system has swapped
Dirty: 1024 kB ← data written to cache, not yet flushed
Real Examples
Is the server actually low on memory?
free -h
# available = 4.4Gi → plenty of memory, ignore the low "free" number
free -h
# available = 312Mi on a 16GB server → memory pressure, investigate
Detect a memory leak
PID=8823
while true; do
echo "$(date +%H:%M:%S): $(ps -p $PID -o rss= | tr -d ' ') KB"
sleep 60
done | tee /tmp/mem_leak.log
If RSS grows steadily without leveling off — memory leak confirmed.
Find what's using swap
# Which processes are using swap
for pid in /proc/[0-9]*; do
pid=${pid##*/}
swap=$(awk '/VmSwap/{print $2}' /proc/$pid/status 2>/dev/null)
[ "${swap:-0}" -gt 0 ] && echo "$swap kB $(cat /proc/$pid/comm 2>/dev/null)"
done | sort -rn | head -10
OOM kill investigation
dmesg | grep -i "oom\|killed process\|out of memory" | tail -10
journalctl -k --since "1 hour ago" | grep -i oom
Common Mistakes
Mistake 1: Panicking at low free memory
free: 200MB on a 16GB server is normal. Linux fills unused RAM with cache. Check available, not free.
Mistake 2: Using VSZ/VIRT for memory analysis A Java process with VIRT=8GB uses ~8GB of address space — not RAM. RES/RSS is actual physical RAM. Always use that.
Mistake 3: Ignoring growing swap
# Watch swap usage over time
watch -n 5 'free -h | grep Swap'
# If "used" column increases: memory pressure growing
Mistake 4: Not checking for OOM kills Process disappeared without a trace? Check dmesg before assuming it crashed.
Pro Tips
# Memory usage summary in one line
awk '/MemTotal/{t=$2} /MemAvailable/{a=$2} END{printf "Used: %.1fGB / Total: %.1fGB (%.0f%% used)\n", (t-a)/1048576, t/1048576, (t-a)*100/t}' /proc/meminfo
# Sort processes by memory, show RSS in human format
ps -eo pid,comm,rss --sort=-rss | head -10 | \
awk 'NR==1{print "PID\tCOMMAND\tRSS"} NR>1{printf "%s\t%s\t%.1fMB\n", $1, $2, $3/1024}'
# Track memory usage of a process over time
pidstat -r -p <pid> 1
# Check NUMA memory distribution (multi-socket servers)
numastat
Conclusion
available in free -h is the number that matters — not free. Sort processes by RES in top (press M) or by rss in ps to find memory consumers. Growing swap + decreasing available = real memory pressure. Static swap with high available = old swap pages from a past spike, not a current problem.
Related: Linux Memory Leak Troubleshooting: RSS vs VSZ Explained — deep dive on tracking memory leaks in production. Linux Performance Troubleshooting: Complete Guide — full diagnostic workflow for memory and CPU together.