Virtual Address Spaces
Virtual memory gives each process the illusion of having its own private, contiguous address space, even though physical memory (DRAM) is shared among all running processes. The operating system and MMU hardware collaborate to maintain this illusion transparently.
Per-Process Address Space
Each process has its own virtual address space, typically laid out as:
- Text segment: the program's compiled machine code (read-only, executable).
- Data/BSS segments: global and static variables.
- Heap: dynamically allocated memory (grows upward via
malloc/new). - Stack: function call frames, local variables (grows downward).
- Kernel space: the top portion of the address space, mapped identically in every process but only accessible in kernel mode.
Two processes can both use virtual address 0x00400000, but their page tables map it to different physical frames. This is the foundation of process isolation -- one process cannot read or corrupt another's memory.
Pages vs. Frames
The virtual address space is divided into pages (virtual) and physical memory is divided into frames (physical). They are the same size (typically 4 KB). A page table maps pages to frames. Not every virtual page needs a corresponding frame -- only pages the process is actively using need to reside in physical memory.
Demand Paging
The OS does not load an entire program into memory at launch. Instead, it uses demand paging: pages are loaded into physical memory only when the process first accesses them. When the CPU accesses an unmapped page, a page fault occurs. The OS page fault handler:
- Determines where the page resides (on disk in the executable, in swap space, or is a zero page).
- Finds a free frame (or evicts an existing page using a replacement algorithm like LRU).
- Reads the page from disk into the frame.
- Updates the page table entry to point to the new frame with the present bit set.
- Restarts the faulting instruction.
Swap Space
When physical memory is full and a new page must be loaded, the OS swaps out a less-recently-used page to a dedicated area on disk called swap space (Linux) or a pagefile (Windows). This allows the system to run programs whose total memory exceeds physical RAM, at the cost of disk I/O when swapped pages are accessed again.
Thrashing and the Working Set
Thrashing occurs when a process (or the system) is spending more time handling page faults than executing useful instructions. This happens when the total memory demanded by active processes far exceeds available physical memory, causing constant swapping.
The working set of a process is the set of pages it actively references during a recent time window. If the OS can keep each process's working set in memory, page faults remain rare. If the combined working sets exceed physical memory, thrashing begins. The OS may respond by suspending processes to reduce memory pressure.
Demand Paging in Action
When you launch a large application like a web browser:
- The OS creates a new process and sets up its page tables, but does not load any code or data into RAM yet. All page table entries are marked "not present."
- The CPU begins executing the first instruction at the entry point. This immediately triggers a page fault because that page is not in memory.
- The OS loads the first code page from the executable file on disk into a free frame, updates the page table, and resumes execution.
- As the program runs, it touches more pages -- each new page triggers a fault and gets loaded on demand.
- If you open a tab but never use certain features (e.g., the PDF viewer), those code pages are never loaded into RAM at all.
Memory overcommit: Linux allows processes to allocate more virtual memory than physical RAM + swap combined. The kernel assumes not all allocated memory will actually be touched. If it is, the OOM (Out-Of-Memory) killer selects a process to terminate.
Impact of thrashing: on a system with 8 GB RAM running workloads needing 16 GB total, the disk (even an SSD) becomes the bottleneck. SSD latency is ~100 microseconds vs. ~100 nanoseconds for DRAM -- a 1000x slowdown on every page fault.