Free — no signup required

Process Memory Analysis: `/proc/[pid]/smaps`

3 min read

The Truth About "Memory Usage"

When top shows a process using 500 MB of RES (Resident) memory, it is not lying exactly — but it is not telling you the whole truth either. The number includes memory that the process is sharing with other processes, counted in full. If 20 processes all load the same libc.so shared library (about 2 MB), top counts those 2 MB against all 20 of them. Add up their RSS values and you get 40 MB attributed to libc.so when only 2 MB of physical RAM is actually used.

To understand the true cost of a process, you need three distinct measurements:

RSS — Resident Set Size
The total physical RAM pages currently mapped into this process's address space, including all shared pages counted in full. This is what top shows. It is the ceiling — the worst-case attribution.

PSS — Proportional Set Size
Private pages counted in full, plus shared pages divided by the number of processes sharing them. If 10 processes share a 10 MB library, each gets 1 MB attributed. PSS is the fair cost — if you add up PSS for all processes, you get a number close to actual physical RAM usage.

USS — Unique Set Size
Only the pages that are private to this process — not shared with anyone. This is the floor — the minimum RAM that would be freed if you killed this process. It is the most useful number for answering "what does killing this process actually give me back?"

The relationship is always: USS ≤ PSS ≤ RSS.

Reading /proc/[pid]/smaps

The kernel exposes per-mapping memory statistics for every process in /proc/[pid]/smaps. Each entry in the file represents one contiguous memory region (a mapped library, the heap, the stack, an anonymous mapping, etc.).

A single entry looks like this:

7f8a2c000000-7f8a2c200000 r--p 00000000 fd:01 1234567  /usr/lib/x86_64-linux-gnu/libc.so.6
Size:               2048 kB    # Virtual address space reserved
Rss:                1024 kB    # Physical pages currently loaded
Pss:                 102 kB    # Proportional share (shared by ~10 processes)
Shared_Clean:        900 kB    # Shared pages not modified
Shared_Dirty:          0 kB    # Shared pages modified (copy-on-write pending)
Private_Clean:       124 kB    # Private unmodified pages
Private_Dirty:         0 kB    # Private modified pages (heap, stack writes)
Referenced:         1024 kB    # Pages accessed recently
Anonymous:             0 kB    # Pages with no file backing
Swap:                  0 kB    # Pages currently swapped out

Summing PSS across all mappings: Since every mapping in smaps reports its own Pss: line, a process's total PSS is just those values summed across every mapping in the file.

The smaps_rollup shortcut:

Since Linux 4.14, /proc/[pid]/smaps_rollup provides pre-summed totals without you having to parse the full file. This is significantly faster for monitoring scripts because the kernel does the aggregation internally rather than generating a potentially multi-megabyte file.

Practical use cases:

  • Container memory limits: When setting memory.limit_in_bytes for a cgroup, use PSS-based measurements to avoid over-provisioning. RSS-based estimates will make your containers appear to need more RAM than they actually do.
  • Memory leak detection: Track USS over time. If USS grows steadily while the process is idle, it has a genuine private memory leak. If only RSS grows, it may just be loading more shared libraries.
  • OOM kill post-mortem: The kernel logs RSS at OOM kill time. Cross-reference with smaps to understand whether the process was genuinely large or just appeared large due to shared library attribution.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free