Chapter 5: Memory Management: Virtual Memory and Pages

Updated

October 31, 2026

Chapter 5: Memory Management: Virtual Memory and Pages

Memory management is frequently the defining factor in overall system throughput and tail latency. Linux provides every userspace process with an isolated, contiguous virtual address space, mapping virtual memory addresses to physical RAM on demand through multi-level hardware page tables.

This chapter breaks down the mechanics of virtual address translation, page faults, kernel allocators, swap behavior, huge pages, and accurate memory accounting.

Virtual Memory: Address Spaces and Page Tables

On modern x86_64 systems, virtual addresses are mapped to physical frames via multi-level page tables managed by the hardware Memory Management Unit (MMU):

  • 4-Level Paging (48-bit VA): 256 TB address space. Walks four levels: PGD (Page Global Directory) \(\to\) P4D \(\to\) PUD (Page Upper Directory) \(\to\) PMD (Page Middle Directory) \(\to\) PTE (Page Table Entry).
  • 5-Level Paging (57-bit VA / LA57): 128 PB address space, adding a fifth translation tier for multi-terabyte systems.
┌────────────────────────────────────────────────────────────────────────┐
│                   4-LEVEL PAGE TABLE TRANSLATION                       │
│                                                                        │
│   Virtual Address: [PGD 9b] [PUD 9b] [PMD 9b] [PTE 9b] [Offset 12b]   │
│                          │         │        │        │         │       │
│   CR3 Register ──► [PGD] ┼─────────┤        │        │         │       │
│                          ▼         ▼        │        │         │       │
│                        [PUD] ──► [PMD] ─────┤        │         │       │
│                                             ▼        ▼         ▼       │
│                                           [PTE] ──► Physical Frame + Off│
└────────────────────────────────────────────────────────────────────────┘

The Translation Lookaside Buffer (TLB)

Walking four levels of page tables in DRAM incurs up to 400 clock cycles of latency per memory access. To mitigate this, processors cache translations in the TLB (Translation Lookaside Buffer). TLB hits resolve in ~1 clock cycle. Minimizing TLB misses is a critical optimization target for memory-intensive applications.

Page Faults: Minor and Major Faults

Linux uses optimistic, on-demand memory allocation. When an application calls malloc() or mmap(), the kernel allocates virtual address space ranges (struct vm_area_struct or VMA), but no physical RAM is assigned until the thread first touches the address, generating a Page Fault exception:

  • Minor Page Fault: The requested memory page is already present in physical RAM (e.g., mapped from the Page Cache, an anonymous zero-fill page, or shared memory). The kernel simply updates the process’s PTE and resumes execution in microseconds.
  • Major Page Fault: The page is not in physical RAM and must be read synchronously from disk or network storage (either from a file-backed binary or swap space). The thread blocks on disk I/O, stalling for milliseconds.
# Observe minor (minflt/s) and major (majflt/s) page faults
sar -B 1 3

Physical Memory: Allocation and Management

Physical memory allocation is managed by two complementary subsystems:

  1. The Buddy Allocator: Manages raw physical memory pages in power-of-two blocks (\(2^0 = 4\text{KB}, 2^1 = 8\text{KB}, \dots, 2^{10} = 4\text{MB}\)). It avoids external fragmentation by splitting and merging adjacent buddy blocks across zones (ZONE_DMA32, ZONE_NORMAL, ZONE_MOVABLE).
  2. The Slab/Slub Allocator: Sits on top of the buddy allocator to allocate small, frequently used kernel data structures (dentries, inodes, socket buffers, task structs) without wasting full 4KB pages.
  3. Memory Folios: Modern Linux (6.x+) abstracts compound pages into folios, guaranteeing contiguous physical memory chunks and drastically reducing internal kernel metadata tracking overhead.
# View active kernel slab allocations
sudo slabtop -s c

Swapping: Mechanics, Costs and Modern Behavior

When memory demand exceeds physical capacity, the kernel reclaims memory:

  • File-backed clean pages are discarded immediately (they can be re-read from disk).
  • Anonymous dirty pages (heap, stack, anonymous mmap) must be written to swap space.
# Key memory reclamation sysctl parameters
cat /proc/sys/vm/swappiness             # Default 60 (aggressiveness of swapping anon vs page cache)
cat /proc/sys/vm/vfs_cache_pressure     # Default 100 (reclaim priority for dentries/inodes)
cat /proc/sys/vm/watermark_scale_factor  # Proximity threshold for waking kswapd background daemon

Modern Swap Best Practice: zswap and zram

Traditional disk swap introduces catastrophic latency spikes. Modern production Linux leverages zswap (a compressed in-memory write-back cache for swap pages) or zram (a compressed RAM block device). Compressing a 4KB page with LZ4/ZSTD in CPU cache requires ~2 microseconds—three orders of magnitude faster than writing to NVMe.

Huge Pages and Transparent Huge Pages

Standard pages are 4KB. A process with 64GB of RAM requires \(16,777,216\) PTEs (128MB of page table overhead) and places immense pressure on the TLB.

  • 2MB Huge Pages: Translates directly at the PMD level, eliminating PTE table walks. 64GB requires only 32,768 entries.
  • 1GB Huge Pages: Translates at the PUD level on supported hardware. Ideal for multi-gigabyte database buffer pools and hypervisor guest memory.

Transparent Huge Pages (THP) vs Explicit HugeTLB

  • Explicit HugeTLB (hugetlbfs): Pre-allocated at boot. Never fragmented, never swapped. Required for high-performance databases (PostgreSQL, DPDK, QEMU/KVM).
  • Transparent Huge Pages (THP): The kernel automatically attempts to allocate 2MB pages for anonymous allocations.

[!WARNING] While THP helps compute workloads, it can introduce latency spikes in latency-critical databases (like Redis or Cassandra) during background page compaction. Set transparent_hugepage=madvise to let applications explicitly opt in via madvise(MADV_HUGEPAGE).

Memory Accounting: RSS, PSS and Kernel Metrics

Evaluating memory consumption using simple ps or top metrics is often misleading due to shared libraries and copy-on-write mappings:

Metric Definition Significance
VIRT (VSZ) Total virtual address space mapped Includes unallocated virtual mappings and open files. Unrelated to actual RAM use.
RSS (Resident Set Size) Physical RAM currently mapped by the process Overcounts memory shared across processes (e.g., shared libc is counted in every process’s RSS).
PSS (Proportional Set Size) Private RAM + (Shared RAM / number of sharing processes) The most accurate metric for individual process physical memory consumption.
USS (Unique Set Size) Private RAM exclusively mapped by this process The exact amount of memory freed if the process terminates.
# View detailed PSS / Private clean and dirty pages for a process
sudo grep -A 12 "^Private" /proc/<PID>/smaps_rollup