2026-06-18
Every time you've added up the RSS column from ps and gotten a number bigger than your actual RAM, that's because RSS double-counts shared pages. Two processes both linked against libc.so? Each one's RSS includes the full library. Sum across hundreds of processes and the books no longer balance.
The kernel has known the answer since 2007: PSS (Proportional Set Size) divides each shared page by the number of processes sharing it, and USS (Unique Set Size) is the memory only this process holds — i.e., what you'd actually reclaim by killing it. Both live in /proc/<pid>/smaps. Almost nothing exposes them sanely. smem does.
Install: apt install smem / pacman -S smem. It's about 1,000 lines of Python.
Top processes by what they actually cost:
$ smem -k -s pss -r | head
PID User Command Swap USS PSS RSS
4821 shaun /usr/lib/firefox/firefox 0 842.3M 891.4M 1.4G
5102 shaun /opt/google/chrome/chrome 0 612.1M 698.7M 1.9G
5103 shaun /opt/google/chrome/chrome 0 410.5M 473.2M 1.7G
Note Chrome: RSS says 1.9G, PSS says 698M. The PSS column is what disappears from free -m if you kill it. The RSS column is fiction.
Group by user, with totals that actually sum correctly:
$ smem -u -k -t
User Count Swap USS PSS RSS
root 89 1.2M 1.4G 1.7G 3.9G
shaun 124 2.1M 5.8G 6.4G 12.1G
www-data 12 0 312.0M 367.0M 624.0M
-----------------------------------------------------------
225 3.3M 7.5G 8.5G 16.6G
Per-library breakdown — find the shared libraries that are actually heavy:
$ smem -m -k -s pss -r | head -5
Map PIDs AVGPSS PSS
[heap] 198 18.2M 3.6G
<anonymous> 847 2.1M 1.8G
/usr/lib/x86_64-linux-gnu/libxul.so 3 201.4M 604.3M
Filter to a process tree, get a real number:
$ smem -k -P chrome -t
PID User Command Swap USS PSS RSS
5102 shaun chrome --type=renderer 0 612.1M 698.7M 1.9G
5103 shaun chrome --type=renderer 0 410.5M 473.2M 1.7G
5099 shaun chrome --type=gpu-process 0 89.2M 102.4M 384.1M
-----------------------------------------------------------------------------
0 1.1G 1.3G 4.0G
The killer feature: charts in your terminal, no DISPLAY required when paired with cairo:
$ smem --pie name -o memory.png # who owns the RAM, visually
$ smem --bar pid -c "command pss uss" # bar chart of top consumers
Running inside a container where /proc/meminfo lies about the host? Pass the real cgroup limit:
$ smem -k --realmem=2G -p # show PSS as % of actual limit
And the most under-used flag: --capture-mode writes a tarball of /proc you can analyze later, on a different machine, with the same tool — perfect for postmortems where the OOM has already happened and you only have a snapshot from sosreport.
Why not pmap, ps, top, or htop? pmap -x shows mappings but no PSS sum. ps/top have no PSS column at all. htop finally added it, but only per-process — no group-by-user, no per-library aggregation, no totals that match free. smem's sums always match reality because the math is correct.
smem reads /proc/<pid>/smaps and gives you PSS and USS, the only per-process numbers that actually add up to your real RAM usage.
