Memory and virtual memory

Address spaces, the stack and heap, paging and page faults, and how swapping and the OOM killer decide what survives.

Inside a virtual address space

Every process believes it owns a contiguous range of memory. The MMU translates each access through page tables to a physical frame, so two processes can use the same virtual address and never collide.

RegionHoldsGrows
TextMachine code, read-onlyFixed at load
Data / BSSInitialised and zero-initialised globalsFixed at load
HeapAllocations from malloc / newUpward
StackFrames, locals, return addressesDownward
Memory-mapped filesLibraries, mmap regionsOn demand
free -h                 # total, used, buff/cache, available
cat /proc/meminfo | head -5
vmstat 1 5              # watch si/so: swap-in and swap-out per second
ulimit -a               # per-process limits, including stack size (Kb)

Paging, faults and the working set

Memory is managed in fixed-size pages (typically 4 KB). A reference to a page that is not resident triggers a page fault: a minor fault if the page is already in memory or can be recreated, a major fault if it must be read from disk.

# faults per second, split minor/major (-s summary)
/usr/bin/time -v ./myprogram 2>&1 | grep -E 'Maximum resident|page faults'
  1. The CPU raises a fault for the missing page and the thread blocks.
  2. The kernel finds or creates the page: from the page cache, a file, or the swap device.
  3. Page tables are updated and the instruction is retried — the program never notices.
  4. If no free frame exists, the page replacement policy picks a victim (LRU approximations) and evicts it.
⚠️
Thrashing is worse than running out of memory: the system spends its time evicting and re-reading pages instead of doing work. If vmstat shows sustained si/so activity, add memory or reduce the working set — do not just increase swap.

Fragmentation and the OOM killer

  • Internal fragmentation wastes the tail of a fixed-size block; external fragmentation leaves free space that is too scattered to use.
  • Linux overcommits memory by default, so a large malloc can succeed and the process is killed only when a page is actually touched.
  • The OOM killer scores processes by memory use and oom_score_adj, then kills the worst offender — often not the one that caused the spike.
  • Memory-mapped files count towards RSS while resident but can be evicted for free, which is why free also reports buff/cache.
  • A container limit is enforced as an OOM condition; exceeding it terminates the container rather than slowing it down.
# who is actually using memory, and how much is reclaimable
ps -eo pid,rss,vsz,comm --sort=-rss | head -10

# is a process leaking, or just caching? watch RSS over time
while true; do ps -o rss= -p 1234; sleep 5; done

FAQ

Why is my process killed with no stack trace?
Almost always the OOM killer. Check dmesg for an out-of-memory message naming the process, then look at peak RSS, container limits, and any unbounded cache.
Does a memory leak always mean lost memory?
No — an ever-growing cache is indistinguishable from a leak in RSS terms. If freeing the cache returns memory and performance stays acceptable, the growth may be intentional.

Processes and threads Files, permissions and I/O

Last refreshed 2026-09-18.