>_ josetilabs.com
← blog

28 Oct 2026 · linux, memory, processos · 4 min

How do you measure a process's memory for real?

RSS counts resident pages, VSZ counts address space, and neither is what the cgroup charges. To get a process’s real cost, read PSS in /proc/PID/smaps_rollup and memory.current in its cgroup. Top and htop show RSS, which counts shared libraries once per process and ignores page cache.

The most common mistake when looking at a process is treating top’s RES as “memory used”. RES is RSS: pages of the process that are in RAM right now. It does not say who shares those pages, does not count the cache the kernel keeps for that process, and does not say whether the address space is only reserved or actually occupied.

What each number measures

Metric Where to read What it counts When to look
VmSize / VSZ /proc/PID/status or statm Mapped address space, including what was only reserved Only to know the theoretical ceiling
VmRSS / RSS /proc/PID/status or statm Resident pages in RAM, shared ones counted in full for each process Quick view in top
PSS /proc/PID/smaps_rollup RSS divided among the processes sharing the page Attributable real memory
USS sum of Private_* in smaps_rollup Private pages of the process, no sharing What you free by killing the process
memory.current /sys/fs/cgroup/…/memory.current RSS + cache + kernel memory of the whole cgroup What the container budget measures

What the cgroup charges

The cgroup is the unit the kernel uses to charge memory to a container or a service. memory.current includes the process RSS, the page cache attributed to that cgroup, and a little kernel memory. That is why a container with a 256 MB limit can be killed by the OOM killer even with 200 MB RSS: cache counts.

If the process runs on the host without its own cgroup, memory.current of the user cgroup mixes everything together and the reading loses meaning. Run the process isolated, in a container or in a systemd unit with MemoryMax, so the accounting closes.

Artifact: measure a process in 30 seconds

The script below measures a PID you pass, or with no argument creates a child that allocates 32 MiB and measures it. It reads /proc, smaps_rollup, and the process cgroup, and prints the five numbers side by side.

bash
#!/usr/bin/env bash
set -euo pipefail

ALLOC_MB="${ALLOC_MB:-32}"
DUR="${DUR:-4}"
PAGINA="$(getconf PAGESIZE 2>/dev/null || echo 4096)"

medir() {
  local pid="$1"
  echo "=== PID $pid ==="
  if [ ! -d "/proc/$pid" ]; then
    echo "processo nao existe"; return 1
  fi
  echo "-- /proc/$pid/status --"
  grep -E '^(Name|VmSize|VmRSS|VmHWM|VmSwap|Threads):' "/proc/$pid/status" || true
  echo "-- /proc/$pid/statm (pages of $PAGINA bytes) --"
  cat "/proc/$pid/statm"
  echo "-- smaps_rollup --"
  grep -E '^(Rss|Pss|Shared_Clean|Private_Clean|Shared_Dirty|Private_Dirty|Anonymous|Swap|Locked):' "/proc/$pid/smaps_rollup" 2>/dev/null || echo "(sem smaps_rollup)"
  echo "-- cgroup --"
  local cg
  cg="$(sed -n 's/^0:://p' /proc/$pid/cgroup 2>/dev/null)"
  echo "cgroup path: ${cg:-?}"
  if [ -n "$cg" ] && [ -f "/sys/fs/cgroup${cg}/memory.current" ]; then
    echo "memory.current: $(cat /sys/fs/cgroup${cg}/memory.current) bytes"
    echo "memory.max:     $(cat /sys/fs/cgroup${cg}/memory.max 2>/dev/null || echo ?)"
    echo "memory.stat rss/cache:"
    grep -E '^(rss|cache|anon|kernel) ' "/sys/fs/cgroup${cg}/memory.stat" 2>/dev/null || true
  fi
  echo "-- ps --"
  ps -o pid,vsz,rss,%mem,comm -p "$pid" 2>/dev/null || true
}

if [ $# -ge 1 ] && [ -n "${1:-}" ]; then
  medir "$1"
  exit 0
fi

echo "Alocando ${ALLOC_MB} MiB num processo filho e medindo..."
(
  x=$(head -c $((ALLOC_MB*1024*1024)) /dev/zero | tr '\0' 'A')
  : "${x:0:1}"
  sleep "$DUR"
) &
child=$!
trap 'kill "$child" 2>/dev/null || true; wait "$child" 2>/dev/null || true' EXIT
sleep 1
medir "$child"
echo
echo "Legenda: VSZ = espaco de enderecos; RSS = paginas residentes; PSS divide compartilhado; cgroup memory.current inclui cache do processo."

Save as mede-memoria.sh and make it executable with chmod +x mede-memoria.sh.

How to verify

Run ALLOC_MB=32 ./mede-memoria.sh. The output should show VmRSS between 90 and 110 MiB (the bash holding the 32 MiB string has its own overhead) and memory.current between 95 and 120 MiB. If memory.current is larger than VmRSS, the difference is cache and kernel memory of the cgroup.

Terminal with the script measuring a child process: VmRSS 97548 kB and memory.current 105308160 bytes
Real output of the script in an isolated container with 256 MB and 0.5 CPU.

If it goes wrong

Which one to use when

If you want to understand why the container limit is a cgroup, read What cgroups are. If the problem is finding who is eating memory on the host, How to see what is eating memory on Linux. To cap a container on purpose, How to limit memory, log, and restart of a container.

FAQ

Are RSS and “memory used” the same thing? No. RSS counts resident pages, including shared ones; it does not count cache per process; it does not say whether the space is reserved or used.

Why is VSZ so large? It is the address space reserved by mmap and malloc. It is not RAM.

PSS or USS to decide whether to kill a process? USS says what you free. PSS is better for comparing processes that share libraries.

Is top not enough? Top is good for finding the suspect. To charge a cost, use the cgroup.

How do you measure a whole container? Read memory.current of the container’s cgroup. That is the number the kernel uses to enforce the limit.

Sources

Read next

// By e-mail

Posts published here also go out by e-mail. Delivery has not started yet: whoever leaves an address now joins the first batch.

I store only your e-mail, the date and the record of this consent. No cookies, no trackers, nothing shared with third parties. Details in the privacy policy.