Virtual Memory

What is it: Each process sees a private, contiguous address space regardless of physical RAM layout. The OS + MMU translate virtual addresses to physical addresses at runtime.

Why: Isolation (process A cannot access process B's pages — hardware-enforced), overcommit (allocate more virtual memory than physical RAM, backed by lazy page allocation), memory-mapped files, simplified linking (each binary always starts at the same virtual address).

Address space layout (64-bit Linux, low → high):

graph BT text["Text (code, read-only)"] --> data["Data (initialized globals)"] data --> bss["BSS (zero-init globals)"] bss --> heap["Heap (grows UP via brk/mmap)"] heap --> mmapregion["Memory-mapped region"] mmapregion --> stack["Stack (grows DOWN, 8MB limit)"] stack --> kernel["Kernel space (not accessible from user mode)"]

Text (code, read-only, shared between same-binary processes) → Data (initialized globals) → BSS (zero-init globals, lazily allocated) → Heap (grows up via brk/mmap) → Memory-mapped region → Stack (grows down, 8MB limit) → Kernel space (not accessible from user mode).

Address translation: MMU uses a 4-level page table on x86-64. Each level indexed by 9 bits of the 48-bit virtual address. TLB caches recent translations. TLB miss = ~10 extra memory accesses. TLB hit = ~1ns overhead.

Worked example — overcommit: a process calls mmap() requesting a 10GB region but only ever reads/writes within a 100MB slice of it. The kernel happily returns success for the full 10GB request — virtual address space is cheap to reserve — but no physical RAM is actually consumed until a page in that region is touched. Checking the process's memory usage afterward, its virtual size (VSZ) shows ~10GB while its resident set size (RSS, physical RAM actually in use) shows ~100MB. This is exactly what makes sparse data structures and generously-sized memory-mapped files practical: reserving address space is nearly free, and physical pages only get allocated on first write, one 4KB page at a time.

Worked example — isolation: two unrelated processes both use virtual address 0x00007f0000000000 for something in their heap. There's no conflict, because each process has its own page table, and the MMU translates that same virtual address to a different physical address depending on which process's page table is currently active (switched by the kernel on every context switch). Process A writing to that virtual address can never corrupt process B's data at the "same" address, because the hardware — not just OS convention — enforces that A's page table simply has no entry pointing into B's physical pages at all.