smem: The Memory Accountant That Stops RSS From Lying to You

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.

Key Takeaway: RSS lies about memory because shared pages get counted N times — 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.

All newsletters