| Tested on | RHEL 10.2 (Coughlan) — vm1.lab.example (7.5 GiB RAM) |
|---|---|
| Package | procps-ng 4.0.4-11.el10sysstat 12.7.6-4.el10util-linux 2.40.2-18.el10stress 1.0.7-5.el10_0 |
| Applies to | Ubuntu, Debian, Kali Linux, Linux Mint, Pop!_OS, Raspberry Pi OS, elementary OS, Zorin OS, Parrot OS, MX Linux, RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora, Arch Linux |
| Privilege | Normal user for free, top, ps, and vmstat; sudo or root for pidstat -r on all processes, slabtop, and swap changes |
| Scope | Diagnose Linux high memory usage with free, top, ps, pidstat, swap, vmstat, slab, and MemAvailable. Does not cover application leak profilers, eBPF, or container cgroup memory limits. |
| Related guides | Check memory usage per process High I/O wait troubleshooting vmstat command top command stress command |
free -h shows used at 2.7 GiB and free at 1.9 GiB on a 7.5 GiB host — that looks tight until you notice available is 4.8 GiB. Linux keeps free RAM low on purpose by caching files, so “high memory usage” alarms often fire on the wrong column.
The steps below separate real pressure from healthy cache, rank the heaviest processes, watch swap and stall signals, peek at kernel slab usage, and decide whether you need more RAM or a process restart.
Understand used vs available memory
Linux accounting splits memory into several buckets:
used— RAM not listed as free (includes much of the page cache viabuff/cache)buff/cache— file cache and buffers the kernel can shrink under pressureavailable— estimate of memory startable workloads can use without swappingfree— completely unused pages (often small on a healthy host)
MemAvailable in /proc/meminfo is the kernel’s best estimate of headroom. Read it when free looks scary but the box feels fine:
grep MemAvailable /proc/meminfoMemAvailable: 5048596 kBAbout 4.8 GiB available on this 7.5 GiB VM means plenty of room despite a modest free line in free -h. Page cache (Cached in /proc/meminfo) is reclaimable — the kernel drops it when applications allocate.
RSS (resident set size) in ps and top is per-process RAM actually in physical memory. VSZ is virtual size mapped address space; it can be huge for Java or databases without meaning every page is resident. Rank by RSS or %MEM when hunting a memory hog.
Check current memory usage
free -h is the fastest summary. Human-readable columns show total, used, free, shared, buff/cache, and available:
free -htotal used free shared buff/cache available
Mem: 7.5Gi 2.7Gi 1.9Gi 5.3Mi 3.3Gi 4.8Gi
Swap: 3.0Gi 0B 3.0Giavailable near several gigabytes with zero swap used is a healthy idle pattern on this lab host. After memory pressure, available drops and swap may fill — that is the pattern to watch during incidents.
Split buffers and cache explicitly with free -w:
free -w -htotal used free shared buffers cache available
Mem: 7.5Gi 2.7Gi 1.9Gi 5.3Mi 4.9Mi 3.2Gi 4.8Gitop adds a live view with an avail Mem field in the header:
top -b -n 1 | head -6MiB Mem : 7679.0 total, 1899.5 free, 2748.7 used, 3332.4 buff/cache
MiB Swap: 3076.0 total, 3076.0 free, 0.0 used. 4930.3 avail MemCompare snapshots before and after deploys or batch jobs — a falling avail Mem line over hours points at a leak or growing cache worth investigating.
Find processes using the most RAM
Sort ps by memory to list the top consumers. %MEM is percentage of physical RAM; RSS is kilobytes resident:
ps aux --sort=-%mem | head -6USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 3471 1.8 8.8 1524940 696284 ? Sl 08:22 1:21 tsserver[5.9.2]: semantic
root 3376 9.6 7.6 86804636 601316 ? Sl 08:21 6:57 node
gdm 1790 0.3 2.9 4502936 230388 tty1 Sl+ 08:21 0:14 gnome-shellTwo processes near nine and eight percent %MEM dominate this snapshot — note PID and command before restarting anything.
top sorted by memory shows the same ranking interactively; batch mode uses -o %MEM:
top -b -n 1 -o %MEM | head -12For per-second sampling during a spike, pidstat -r reports resident and virtual kilobytes:
pidstat -r 1 2During stress --vm 2 --vm-bytes 2G, the stress workers topped the list:
Average: 0 23675 118053.47 121.29 2100308 1952844 24.84 stress
Average: 0 23676 117699.01 22.77 2100308 1915136 24.36 stressEach stress line shows roughly 1.9 GiB RSS — matching the two 2 GiB allocations under pressure. Sample for at least thirty seconds; short-lived cron jobs disappear from a one-shot ps.
Inspect one PID through /proc when you need file-backed versus anonymous breakdown:
grep -E '^(VmRSS|VmSize|RssAnon|RssFile):' /proc/3376/statusVmSize: 86804636 kB
VmRSS: 601316 kB
RssAnon: 537364 kB
RssFile: 63952 kBLarge RssAnon with growing VmRSS over time on a long-running service suggests a heap leak — see check memory usage per process for deeper per-process tooling.
Check swap activity
Swap extends RAM to disk when physical memory is exhausted. Unused swap on a healthy host is normal:
swapon --showNAME TYPE SIZE USED PRIO
/dev/dm-1 partition 3G 0B -2vmstat columns si (swap in) and so (swap out) show paging per second — zeros mean no swap traffic:
vmstat 1 2r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 1944808 5052 3407288 0 0 733 1698 871 4 2 6 91 0 0
0 0 0 1944788 5052 3407296 0 0 0 32 515 468 0 2 97 0 0Under stress --vm pressure on the lab host, start allocators and sample vmstat again while they run:
stress --vm 2 --vm-bytes 2G --timeout 12During that run, swpd grew and si/so turned non-zero:
vmstat 1 22 0 193356 171444 168 1157500 55256 59232 55256 59232 5210 4407 0 56 8 36 0Non-zero si/so sustained while applications lag means RAM is oversubscribed — add memory, reduce caches, or stop the heaviest process. Heavy swap also drives high I/O wait because paging is disk I/O.
After the stress test exited, swap partially remained in use until the kernel reclaimed it:
free -hSwap: 3.0Gi 32Mi 3.0GiA few megabytes of swap used after pressure is not always an emergency; sustained growth over days is.
Check memory pressure
Memory pressure means the kernel struggles to satisfy allocations — swap rises, reclaim runs often, and latency spikes.
Watch MemAvailable fall during load. Before stress --vm:
grep MemAvailable /proc/meminfoMemAvailable: 5048596 kBDuring allocation pressure, free -h showed available down to about 3.3 GiB and free under 400 MiB — tighter headroom even before heavy swapping.
On kernels with PSI (Pressure Stall Information), one-line averages show how long tasks waited on memory:
cat /proc/pressure/memoryWhen PSI is enabled, output looks like some avg10=2.50 rising under load — higher avg10 means more stalls waiting for RAM. This lab kernel does not expose /proc/pressure/memory; use falling MemAvailable and non-zero si/so in vmstat instead.
vmstat column r (runnable processes) climbing with low free and non-zero so is another pressure signal — the kernel spends time reclaiming and paging instead of running your code.
Check kernel memory
Not all “missing” RAM sits in user processes. Kernel slab caches appear in /proc/meminfo:
grep -E '^Slab:|^SReclaimable:|^SUnreclaim:' /proc/meminfoSlab: 392040 kB
SReclaimable: 272360 kB
SUnreclaimable: 119680 kBSReclaimable can shrink under pressure; SUnreclaimable cannot — growth here with flat user RSS points at kernel-side usage (network stacks, many mounts, driver caches).
slabtop ranks slab consumers by cache size:
slabtop -o -s c | head -10OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
142160 142160 100% 1.00K 8885 16 142160K xfs_inode
328650 328532 99% 0.19K 15650 21 62600K dentry
55286 55219 99% 0.57K 3949 14 31592K radix_tree_nodeLarge xfs_inode or dentry caches on file-heavy servers are often normal. Sudden slab growth after a kernel upgrade or driver change deserves a dmesg review.
PageTables and KernelStack in /proc/meminfo track per-process kernel overhead — many thousands of threads inflate these even when each process RSS looks small.
Determine whether memory usage is actually a problem
Match symptoms to signals — low free alone is not a problem statement.
| What you see | Likely meaning | What to do |
|---|---|---|
Low free, high available, no swap |
Healthy page cache | No action; cache recycles automatically |
Falling MemAvailable, rising RSS on one PID |
Runaway process or leak | Inspect /proc/PID/status; restart or patch app |
Non-zero si/so in vmstat, apps slow |
RAM exhaustion + paging | Add RAM; reduce footprint; tune service limits |
High %MEM many Java/node PIDs |
Expected heap footprint | Tune JVM/container limits; confirm against sizing guide |
High SUnreclaim, modest user RSS |
Kernel slab growth | slabtop; patch kernel/driver; reduce socket/mount churn |
OOM kills in dmesg |
Hard limit hit | Increase RAM or lower vm.overcommit-sensitive workloads |
| Pressure only after deploy | New version regression | Roll back; compare ps RSS before and after |
After you stop or fix a heavy allocator, confirm memory recovered:
free -hMem: 7.5Gi 2.9Gi 3.8Gi 3.1Mi 1.1Gi 4.6Gi
Swap: 3.0Gi 32Mi 3.0Giavailable back above 4 GiB and swap nearly empty after stress exited is the recovery shape you want. If available stays low with quiet swap, look for unreclaimable slab or mapped but idle VSZ from services that mmap large regions.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
free near zero, available healthy |
Page cache in use | None required unless latency proves otherwise |
| One PID RSS grows for days | Memory leak | Restart mitigates; fix application; profile in lower env |
Swap fully used, constant si/so |
Undersized RAM | Add memory; reduce buffers; stop duplicate cron jobs |
High memory, low CPU, high wa |
Swap thrash to disk | Same as RAM shortage; see high I/O wait |
dmesg OOM killer messages |
cgroup or global limit | Raise limit or reduce workload; check dmesg -T |
| Many processes, small RSS each | Over-provisioned instance count | Consolidate or scale out with proper limits |
| Slab cache hundreds of MB | Many files or sockets open | Expected on busy file servers; investigate if sudden |
References
- free(1) — Linux manual page
- proc(5) — Linux manual page (
/proc/meminfo) - ps(1) — Linux manual page
- pidstat(1) — Linux manual page
- vmstat(8) — Linux manual page
Summary
High memory usage on Linux is often misread from the free column alone. available and MemAvailable tell you how much RAM new work can still take; buff/cache is reclaimable file cache, not wasted space.
Rank suspects with ps --sort=-%mem, top, and pidstat -r during the incident — lifetime averages in ps miss short spikes. Swap columns in free and si/so in vmstat show when the kernel pages to disk; sustained swap traffic with falling available memory means you are past comfortable headroom.
Kernel slab and unreclaimable pages explain memory that does not appear in application RSS. Use the decision table to separate healthy cache from leaks, undersized RAM, and kernel growth — then add memory or fix the process that actually owns the footprint.

