Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

memstat

A Linux snapshot of where memory actually lives: RAM (plain + zswap) and disk swap. It is a Python 3 script that reads /proc and, optionally, zswap's root-only debugfs counters. Stdlib only.

memstat.py              one-shot snapshot
memstat.py -w [N]       refresh every N seconds (default 2), with rates
memstat.py -i           annotate derived figures with footnotes
memstat.py --install    grant passwordless reads of the zswap pool counters
memstat.py --uninstall  revoke that grant
memstat.py --selftest   run fixture-driven checks of the bucket maths
memstat.py -h           this help

Run it from this directory (./memstat.py) or put it on your PATH.

Installation

The installation mostly involves copying memstat.py to your path, but some of the zswap instrumentation needs additional permissions.

zswap --install

The pool size lives in root-only debugfs (/sys/kernel/debug/zswap/{pool_total_size,stored_pages,written_back_pages}). --install writes a visudo-checked sudoers rule for exactly those three cat invocations, then verifies a passwordless read. Without it the zswap line is blank, SWAP on disk is overstated, and zswap_freed renders -.

--uninstall removes the rule. Run both as your normal user, not root.

Requirements

Python 3 (stdlib only) and Linux with /proc/meminfo, /proc/vmstat, and /proc/pressure. zswap is optional; if it is disabled the tree says so. Colour needs a terminal that understands ANSI SGR and the / glyphs.

Explaining the UI

What the tree is saying

The top panel is a hierarchy, not a list of independent meters.

MEMORY IN USE     used / (MemTotal + SwapTotal)
├─ RAM            used / MemTotal          (MemTotal − MemAvailable)
│  ├─ non-zswap   RAM minus the zswap pool
│  └─ zswap       compressed anon sitting in RAM
└─ SWAP on disk   swap slots that actually reached disk

MEMORY IN USE is RAM plus disk swap. Pages held in zswap still occupy a swap slot, but they have not hit the disk, so they are counted under RAM (the pool) rather than under SWAP on disk. Without zswap stats, that disk figure is overstated.

The stacked bars are categories, not locations:

  • magenta / anon — No file behind it. Includes shmem (/dev/shm). Anything in zswap or on disk swap is anon — file pages are written back and dropped, never swapped.
  • cyan / file — Page cache + buffers, with shmem removed so it is not counted twice.
  • yellow / kernel — The remainder of RAM after free, anon, file, and the zswap pool.
  • dim / free — Unused capacity of that row.

Percentages go green / yellow / red at 70% and 90%. zswap's compression ratio is coloured separately: green at ≥3×, yellow at 2–3×, red below 2× (zstd on ordinary application memory usually lands around 3–4×; below 2× the CPU cost is mostly wasted).

committed is Committed_AS / CommitLimit. A multiple well above 1× means the kernel has promised more anonymous memory than it can back, which is how a machine enters reclaim livelock rather than a clean OOM kill.

zswap and non-zswap compete for the same RAM. Their denominators are live:

  • non-zswap capacity = MemTotal − current pool size, so it shrinks as the pool grows.
  • zswap capacity = min(max_pool_percent of MemTotal, SwapTotal / compression ratio). Every page in the pool also consumes a swap slot, so at good compression the slot bound bites before max_pool_percent does.

The rate table

Values are pages (4 KB) per interval, except pgmajfault, which counts faults (~4 pages each). - means history has not reached that far yet.

Rates need sample history. Every run appends a line to $XDG_RUNTIME_DIR/memstat-py.$UID.hist (tmpfs; discarded at reboot, matching the counters themselves). A cold one-shot shows totals only; windows fill in as you run it again or leave -w going. History is kept for 150 seconds.

Four groups:

cost of reclaimpgsteal_direct is the kernel stalling to free RAM under the allocating process. Any non-zero rate here is a stall.

OUT of RAM — evicted — anon leaving RAM: into zswap, from zswap onto disk, or written to swap by any path.

INTO RAM — faulted in — file pages returning, anon coming back from zswap (fast) or from disk swap (slow), and major faults. pgmajfault also covers first-ever reads of file pages that were never resident; the three rows above it are genuine returns.

FREED — anon claim dropped, not evicted — a process actually gave the page up: it exited, unmapped, or freed. That is the opposite of eviction (the OUT rows), where the process still owns the page and the kernel only pushed it into zswap or onto disk.

The three FREED rows watch that same anonymous page (heap, stack, /dev/shm) in each place it can sit:

  • anon_freed — uncompressed in RAM
  • zswap_freed — compressed in the zswap pool
  • swap_freed — a slot on disk swap

There is no page-cache row. File cache is leftover RAM the kernel filled on its own. Nobody asked to keep it, so a falling cache number is not a release — it is housekeeping. On an idle machine that is ~10k pages/s, and it would bury the FREED signal.

FREED is derived from occupancy gauges, not counted. Eviction (OUT rows) is subtracted so a page leaving RAM because it was swapped out does not read as a release. Within one sample interval (~2s) a concurrent allocation still cancels a release; across intervals they do not.

Colour on the rate table is pages/sec, so buckets of different widths are judged on the same scale. The FREED rows invert that: they stay dim below a deadband (ordinary idle churn) and go green when a real release is happening.

Pressure stalls

/proc/pressure for cpu, memory, and io, as %time stalled over 10s / 60s. some means at least one task stalled. Memory full means every non-idle task stalled at once — the signature of a reclaim livelock rather than mere slowness. That figure is highlighted when it is non-trivial.

About

A linux utility for monitoring system memory usage and pressure in real-time.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages