Tools to Inspect the Linux Process Memory Map
Go beyond /proc/PID/maps with pmap, smem, and procmap. Learn what each tool shows, when to use it, and how to interpret physical memory metrics like RSS, PSS, and USS.
From Raw Data to Useful Insights
In Part 1 we learned how to read the raw /proc/PID/maps file and understand what every field means. That raw output is powerful but dense. In practice, engineers use higher-level tools that parse this data and present it in a more useful way. This tutorial covers the three most important ones: pmap, smem, and a third tool that visualises both kernel and user VAS together.
The Detailed View: /proc/PID/smaps
You already know that /proc/PID/maps gives you one line per memory segment. Linux also provides a much more detailed version at /proc/PID/smaps. For each segment in the maps file, smaps adds a block of detailed physical memory statistics.
Try running this in your terminal to see it for yourself:
$ cat /proc/self/smaps
You will see blocks like this for every segment:
7f3a2c000000-7f3a2c400000 rw-p 00000000 00:00 0
Size: 4096 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 100 kB
Pss: 50 kB
Shared_Clean: 80 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 20 kB
Referenced: 100 kB
Anonymous: 100 kB
...
The man proc page documents every field. The key ones you need to understand are RSS, PSS, and USS — three ways of measuring how much physical RAM a mapping is actually using.
Understanding RSS, PSS, and USS
When a file like libc.so is loaded into memory, it is often shared between dozens of processes. If you simply add up “how many bytes is this process using”, you get an inflated number because the shared pages are counted multiple times. RSS, PSS, and USS are three progressively more accurate ways of answering “how much RAM is this process actually consuming?”
Scenario: libc.so (400 KB) is shared by 4 processes. Process A also has 100 KB of private anonymous memory.
KB
KB
KB
KB
KB
| Metric | Full Name | Value for Process A | How It Is Calculated |
|---|---|---|---|
| RSS | Resident Set Size | 500 KB | All physical pages currently in RAM for this process, counting shared pages in full. Overestimates shared cost. |
| PSS | Proportional Set Size | 200 KB | Private pages + (shared pages ÷ number of sharers). 100 KB private + (400 KB ÷ 4) = 200 KB. Fairer measure. |
| USS | Unique Set Size | 100 KB | Only private pages. Memory that would be freed if this process was killed. Most conservative measure. |
RSS is what you see in top and ps. It is quick but overestimates. PSS is the most useful metric for capacity planning — it fairly distributes shared costs. USS tells you the real cleanup impact if a process is killed. Use PSS for comparing processes, USS for leak detection.
pmap — Your Primary Memory Map Viewer
pmap is a standard Linux utility that presents the process memory map in a clean, readable format. It reads from /proc/PID/maps (and optionally /proc/PID/smaps) and formats the output as a table with column headers, making it much easier to scan than the raw file.
Basic usage:
# Show memory map for process with a specific PID
$ pmap <PID>
# Show extended information (adds RSS per mapping)
$ pmap -x <PID>
# Show the most detailed output available
$ pmap -XX <PID>
The -x flag adds a column showing the RSS for each individual mapping — very useful for tracking which shared library is consuming the most physical memory. The -XX flag dumps everything that the kernel provides in /proc/PID/smaps, which is the maximum detail level you can get from a standard tool.
| Column | Example | What It Tells You |
|---|---|---|
| Address | 0000555d83b65000 | Start of the virtual address region |
| Kbytes | 12 | Size of the virtual region in kilobytes |
| RSS | 8 | Physical RAM pages actually loaded (in KB) |
| Dirty | 0 | Pages modified since loading (need writeback) |
| Mode | r-x– | Permissions for this mapping |
| Mapping | /bin/cat | Source file or label for this mapping |
A common beginner mistake is to assume that “virtual size = RAM used”. As the diagram shows, the virtual reservation can be much larger than the physical footprint. Linux uses demand paging — it only loads pages into physical RAM when they are actually accessed.
smem — Finding the Actual Memory Hog
smem solves a very specific but common problem: which process on my system is actually consuming the most physical memory? If you use RSS for this comparison, shared libraries count multiple times and the answer is misleading. smem uses PSS and USS, which give you a much more honest picture.
Unlike pmap, which focuses on the segments of a single process, smem works across all processes simultaneously and answers the system-wide question.
Question answered: What are all the memory segments of this specific process and how large is each one?
Use when: debugging a single process, finding which library takes up space
Question answered: Which process across the entire system is consuming the most real physical RAM?
Use when: investigating memory pressure, finding resource hogs, capacity planning
Install and run smem:
# Install on Debian/Ubuntu/Raspberry Pi OS
$ sudo apt install smem
# Show all processes sorted by PSS (most fair comparison)
$ smem --sort pss
# Show only the top 10 memory consumers
$ smem --sort pss | tail -10
# Output columns: PID, User, Command, Swap, USS, PSS, RSS
Bar length represents relative size. RSS is always the largest, USS the smallest.
smem reports on physical memory (pages in RAM). It does not directly show you the virtual address layout of a process the way pmap does. Think of them as complementary tools: use pmap for virtual layout, smem for physical footprint.
Visualising Both User and Kernel VAS
So far, all the tools we have discussed focus on the user-space half of the virtual address space. But every process actually has two halves: user space (where your application code and libraries live) and kernel space (where the kernel image, kernel stacks, module memory, and direct-mapped RAM live).
pmap and smem only show you the user-space half. To see both halves together for any given process, you can use extended utilities or read /proc/PID/smaps alongside kernel-specific pseudo-files. Some debugging-oriented tools (available as open-source projects on GitHub) provide a vertically tiled visualisation of the complete VAS — both user and kernel — in a single view ordered by descending segment size.
The tools you use in practice (pmap, smem, /proc/PID/maps) all show you the bottom half of this picture — the user VAS. Understanding that there is an equally important kernel half — and that the kernel is mapped into every process’s address space (but inaccessible from user mode) — is key to understanding how system calls work and why mode switching has a cost.
Practical Workflow: Diagnosing Memory Issues
Now that you understand the tools, here is a practical decision flow for common memory investigation tasks:
smem --sort pss — it gives you a fair comparison across all processespmap -x <PID> — see each mapping with RSSsmem USS over time — if USS keeps growing, there is a leak/proc/PID/smaps directly — richest data source/proc/PID/maps — non-zero means file-backedTry this on your Linux machine (any modern distro works):
# Step 1: Open a terminal and start a background sleep process
$ sleep 600 &
[1] 12345 # note the PID (yours will be different)
# Step 2: Look at its raw memory map
$ cat /proc/12345/maps
# Step 3: Use pmap for a cleaner view
$ pmap -x 12345
# Step 4: See detailed per-segment stats
$ cat /proc/12345/smaps | head -60
# Step 5: Check how the sleep binary inode matches on disk
$ ls -i $(which sleep)
# Step 6: Clean up
$ kill %1
For step 5, compare the inode number from ls -i against what you see in the /proc/PID/maps output for the /usr/bin/sleep line. They will match — confirming how the VFS inode ties the VAS entry to the file on disk.
Interview Questions on This Topic
These types of questions appear frequently in embedded Linux and kernel engineering interviews at companies like Texas Instruments, Qualcomm, and STMicroelectronics:
mmap(MAP_ANONYMOUS). These regions are backed only by physical RAM (and potentially swap), not by a file.gettimeofday and clock_gettime) that only need to read kernel data. By mapping this code into user space, the kernel avoids the overhead of a full mode switch for these frequent calls. Unlike the older vsyscall mechanism, vDSO maps at a randomised address and is fully ASLR-compatible.0x0000_xxxx_xxxx_xxxx). Kernel virtual addresses have their top 16 bits all set to one (the address looks like 0xffff_xxxx_xxxx_xxxx). The vsyscall page is a special case — it is a kernel page mapped at a fixed high address, which is why its top bits are set.🎯 Key Takeaways from Part 2
Continue Your Free Linux Kernel Journey
EmbeddedPathashala offers completely free courses on Linux kernel programming, Linux device drivers, and Bluetooth/BLE development. No fees, no sign-ups required.
Browse All Free Courses