Who's Eating Your Swap? Diagnosing Swap Usage with smem
TL;DR: free -h tells you swap is full. top won't tell you who filled it. smem reads /proc/PID/smaps, sums the Swap: field per process, and hands you the hog's name in one command: smem -k -rs swap. You'll need a Debian/Ubuntu desktop and sudo; smem installs straight from apt.
The laptop had 16 GB of RAM and 8 GB of swap. It still took forty seconds to switch windows. The fan spun like a jet engine at idle, and every app I touched felt like it was reading from a disk that had given up. I knew the box was swapping. I had no idea what was sitting in the swap.
free -h showed the totals. top showed a wall of RSS numbers. Neither answered the only question that mattered: which process, and why. That's the gap smem fills, and it's the reason this article is a head-to-head: four tools, one question, one winner.
Quick house rule before the fight: diagnose your own machines, or boxes you're paid to look at. Swap forensics on someone else's server without a contract is a career-ending move. Everything below assumes the box is yours.
The Contenders
Four tools claim to tell you about swap. Three of them are lying, in the sense that they tell you less than you need:
| Contender | What it tells you | What it hides |
|---|---|---|
free -h | Total swap used and free | Everything per-process |
top | Per-process RSS, CPU | Swap (the SWAP column is off by default) |
swapoff -a | Total swap in use, then forces it back to RAM | Which process — and it can OOM you |
smem | Per-process USS, PSS, RSS and swap, sortable | Nothing relevant — but needs root for the full picture |
/proc by hand | VmSwap per process | No totals, no sorting, no shared-memory math |
The Rules of the Fight
A tool wins a round if it answers the question without making me read three man pages. The question is always the same: who is in my swap, and how much? Speed matters, but attribution matters more. A total without a name is a symptom, not a diagnosis.
Round 1 — free -h: Totals, and Nothing Else
free -h parses /proc/meminfo and formats it for humans. It's the right first move, and it's the wrong last move:
free -h
That gives you the Swap line: total, used, free. Useful as a yes/no — is the box swapping? — and useless for everything after. I spent a week watching swap usage climb every afternoon on a lab desktop, convinced some daemon was leaking. The answer turned out to be a browser tab farm. free couldn't tell me that, because it can't see processes at all.
Round 2 — top: RSS, Not Swap
top sorts by CPU by default, and its memory column is RSS, which double-counts shared pages. RSS is also not swap: a process can sit at 2 GB RSS and zero swap, or the reverse.
procps-ng top does have a SWAP column — press f in the interactive view and enable it — but it's off by default, and it reads VmSwap from /proc/PID/status, a single number per process. It works, and it's clunky. You'll re-enable it every session, and you still get no totals, no percentages, no shared-memory accounting.
Honest verdict: top is where you notice the symptom, not where you solve it.
Round 3 — swapoff -a: The Sledgehammer
Somewhere in every swap discussion, someone suggests swapoff -a to "clear the swap." Let's be precise about what that does: it tells the kernel to move every swapped page back into physical RAM. On a box where swap usage exceeds free RAM — which is exactly the situation that made you look — that ends with the OOM killer picking victims.
I ran it once on a lab box with 4 GB in swap and 1.5 GB free. The OOM killer had opinions, and my editor was one of them. Don't do this on a machine you like. It answers "how much was in swap" (read /proc/meminfo before and after) and nothing about "who."
Round 4 — smem: The One That Answers "Who"
smem is a Python tool by Matt Mackall, packaged in Debian and Ubuntu since 2011. It reads /proc/PID/smaps for every process, sums the Swap: field across all mappings, and adds the USS/PSS/RSS trio. The default columns already include swap: PID User Command Swap USS PSS RSS.
Install it:
sudo apt install smem
That pulls Python 3 and, as a recommended package, python3-matplotlib — you'll want it for the charts later. Now the money command:
sudo smem -k -rs swap
Break that down: -k abbreviates to human units (M, G), -s swap sorts by the swap column, -r reverses the sort. Without -r the sort is ascending, which parks your biggest swap consumer at the bottom of the list — the one place you won't look. With it, the hog sits at the top, and you get output like:
PID User Command Swap USS PSS RSS 2143 you firefox 1.2G 890.0M 1.1G 1.6G 1608 you gnome-shell 180.0M 92.0M 110.0M 150.0M 1021 root systemd-journald 24.0M 3.0M 4.5M 6.0M
That's the whole diagnosis in one screen: one process owns the swap, and it's the browser with 40 tabs. The fix is a restart, a tab purge, or a conversation with your own browsing habits.
Totals and Percentages
Add -t for a totals row and -p for percentages. The swap percentage is measured against SwapTotal from /proc/meminfo:
sudo smem -t -p -k -rs swap
If your box has no swap at all, the percentage column reads N/A — smem divides by zero and says so politely.
The System View
-w switches from processes to the whole machine: firmware/hardware, kernel image, kernel dynamic memory, userspace, and free. It's the answer to "is this box even close to the edge?" before you start blaming individual processes. One caveat: the firmware/hardware and kernel-image rows read 0 unless you pass -R <RAM> and -K /path/to/vmlinux — upstream's own example is smem -R 4G -K /path/to/vmlinux -w:
sudo smem -w
The Charts
smem can draw. --pie name -s swap renders a pie of swap by process name; --bar name -s swap gives you bars. Both need matplotlib, which the apt install already recommended:
sudo smem --pie name -s swap
sudo smem --bar name -s swap
The pie is the one that lands in a report: one fat slice labeled firefox, and the meeting ends early. If smem complains that a chart "requires matplotlib," install it explicitly with sudo apt install python3-matplotlib.
The /proc Comparison: smem Is the Manual, Automated
Nothing about smem is magic. Every number it prints comes from files you can read yourself. The per-process total lives in VmSwap inside /proc/PID/status:
grep -E "Name|VmSwap" /proc/$(pgrep -n firefox)/status
And the per-mapping breakdown lives in /proc/PID/smaps, where each mapping carries its own Swap: line. Sum them by hand:
grep Swap: /proc/$(pgrep -n firefox)/smaps | awk '{s+=$2} END {print s " kB"}'
Now do that for every PID on the box, sort the results, add totals and a percentage column, and you've rebuilt smem. That's the honest summary of what the tool is: the manual, automated. Worth knowing, because it tells you where to look when smem itself misbehaves.
One more comparison while we're here: systemd's memory tooling. systemd-cgtop shows cgroup-level memory, which is great for containers and slices, but it has no per-process swap column, and systemd-analyze is boot-time analysis, not swap-time. Neither answers our question. smem does.
Healthy Swap vs a Thrashing Box
Swap itself isn't the enemy. A healthy desktop swaps occasionally — a suspend/resume cycle, a compile that overshoots, a browser that got greedy for an hour. The numbers move, then come back down. That's the kernel doing its job.
Thrashing looks different. Swap usage climbs and stays. The UI stutters because the kernel is trading pages in and out faster than the disk can serve them. Watch the si and so columns of vmstat 1: sustained nonzero values mean the box is fighting itself.
vmstat 1 5
When that happens, smem gives you the culprit in one screen. The fix follows the diagnosis: restart the hog, fix the leak, close the tabs. If the box genuinely lacks RAM, the honest fix is more memory or zram — not a lower vm.swappiness prayer, which only delays the same conversation.
The Verdict
| Tool | Best for | Verdict |
|---|---|---|
free -h | A yes/no: is the box swapping? | Round 1, loses on attribution |
top | Spotting the symptom mid-session | Round 2, SWAP column hidden |
swapoff -a | Forcing the issue on a box you can lose | Round 3, disqualified: OOM risk |
smem | Naming the process that eats swap | Winner |
Pick free -h when you need a yes or no. Pick top when you're already in it. Pick swapoff -a when you're rebooting anyway and can afford collateral damage. Pick smem when the question is "who" — which is every time you got here in the first place.
Sharp Edges
- Root changes the picture. Without sudo, smem can only read processes you own —
/proc/PID/smapsrefuses everyone else. Run it with sudo or you'll diagnose a ghost. - The sort is ascending by default.
-s swapalone parks the hog at the bottom.-ris not optional. - Charts need matplotlib. The apt install recommends it, but if you used
--no-install-recommends, the pie and bar will refuse politely.sudo apt install python3-matplotlibfixes it. - No swap, no percentages.
-pdivides bySwapTotal; with no swap configured, the column readsN/A. - Version trap. Distros ship smem 1.5, the Python 3-compatible release. The old 1.4 tarball on the upstream site is Python 2 code — if you hand-roll it,
--barcrashes withNameError: name 'xrange' is not defined. - Snapshots, not truth. The kernel docs warn that reading
smapsis inherently racy. smem gives you a coherent snapshot, not an atomic guarantee.
What Stuck
free -h: totals only. Fine for yes/no, blind to who.top: RSS, not swap. The SWAP column exists and is off by default.swapoff -a: the sledgehammer. It can OOM a box that's already in trouble.sudo smem -k -rs swap: the answer, in one line.sudo smem -t -p -k -rs swap: the answer with totals and percentages.- smem reads
/proc/PID/smapsand sumsSwap:— you can do it by hand, but you won't want to.
Go deeper: Essential Linux Tools After a Fresh Install for the rest of the monitoring stack · The Linux /proc Filesystem: Troubleshoot Any Desktop for the files smem reads under the hood · smem on Termux: Diagnosing Memory Pressure Without Root for the same question on a phone, where the rules change completely.
