Troubleshoot High Memory Usage in Linux

Tested on RHEL 10.2 (Coughlan) — vm1.lab.example (7.5 GiB RAM)
Package procps-ng 4.0.4-11.el10
sysstat 12.7.6-4.el10
util-linux 2.40.2-18.el10
stress 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 via buff/cache)
  • buff/cache — file cache and buffers the kernel can shrink under pressure
  • available — estimate of memory startable workloads can use without swapping
  • free — 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:

bash
grep MemAvailable /proc/meminfo
output
MemAvailable:    5048596 kB

About 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:

bash
free -h
output
total        used        free      shared  buff/cache   available
Mem:           7.5Gi       2.7Gi       1.9Gi       5.3Mi       3.3Gi       4.8Gi
Swap:          3.0Gi          0B       3.0Gi

available 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:

bash
free -w -h
output
total        used        free      shared     buffers       cache   available
Mem:           7.5Gi       2.7Gi       1.9Gi       5.3Mi       4.9Mi       3.2Gi       4.8Gi

top adds a live view with an avail Mem field in the header:

bash
top -b -n 1 | head -6
output
MiB 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 Mem

Compare 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:

bash
ps aux --sort=-%mem | head -6
output
USER         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-shell

Two 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:

bash
top -b -n 1 -o %MEM | head -12

For per-second sampling during a spike, pidstat -r reports resident and virtual kilobytes:

bash
pidstat -r 1 2

During stress --vm 2 --vm-bytes 2G, the stress workers topped the list:

output
Average:        0     23675 118053.47    121.29 2100308 1952844  24.84  stress
Average:        0     23676 117699.01     22.77 2100308 1915136  24.36  stress

Each 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:

bash
grep -E '^(VmRSS|VmSize|RssAnon|RssFile):' /proc/3376/status
output
VmSize:	86804636 kB
VmRSS:	  601316 kB
RssAnon:	  537364 kB
RssFile:	   63952 kB

Large 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:

bash
swapon --show
output
NAME      TYPE      SIZE USED PRIO
/dev/dm-1 partition   3G   0B   -2

vmstat columns si (swap in) and so (swap out) show paging per second — zeros mean no swap traffic:

bash
vmstat 1 2
output
r  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  0

Under stress --vm pressure on the lab host, start allocators and sample vmstat again while they run:

bash
stress --vm 2 --vm-bytes 2G --timeout 12

During that run, swpd grew and si/so turned non-zero:

bash
vmstat 1 2
output
2  0 193356 171444    168 1157500 55256 59232 55256 59232 5210 4407  0 56  8 36  0

Non-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:

bash
free -h
output
Swap:          3.0Gi        32Mi       3.0Gi

A 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:

bash
grep MemAvailable /proc/meminfo
output
MemAvailable:    5048596 kB

During 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:

bash
cat /proc/pressure/memory

When 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:

bash
grep -E '^Slab:|^SReclaimable:|^SUnreclaim:' /proc/meminfo
output
Slab:             392040 kB
SReclaimable:     272360 kB
SUnreclaimable:   119680 kB

SReclaimable 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:

bash
slabtop -o -s c | head -10
output
OBJS 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_node

Large 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:

bash
free -h
output
Mem:           7.5Gi       2.9Gi       3.8Gi       3.1Mi       1.1Gi       4.6Gi
Swap:          3.0Gi        32Mi       3.0Gi

available 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


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.


Frequently Asked Questions

1. Why is Linux using so much memory when free is low?

Linux uses spare RAM for page cache and buffers to speed up disk reads. Low free with high buff/cache and healthy MemAvailable is normal — the kernel reclaims cache when applications need RAM. Worry when MemAvailable drops, swap use climbs, or applications get OOM-killed.

2. How do I find which process uses the most memory on Linux?

Run ps aux --sort=-%mem or top sorted by memory. pidstat -r 1 5 shows RSS and virtual memory per PID every second. Read RSS for resident RAM and VSZ for mapped address space — a large VSZ with small RSS is not always a leak.

3. What is the difference between used and available memory in free?

Used includes application RAM plus much of the page cache counted in buff/cache. Available estimates how much memory can be given to new workloads without swapping. Trust available more than free on modern Linux.

4. When should I add more RAM to a Linux server?

Add RAM when MemAvailable stays low under normal load, swap in and out stay non-zero in vmstat, applications hit OOM kills, or latency spikes correlate with memory pressure — not merely because free is small while available is still several gigabytes.

5. Can high kernel memory cause low available RAM?

Yes. Slab caches, unreclaimable kernel structures, and many network sockets can grow SUnreclaim in /proc/meminfo. Use slabtop and compare Slab to MemAvailable. User-space ps totals may look modest while kernel usage is high.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)