Why This Matters
Every process on your computer believes it has access to a vast, contiguous block of memory all to itself. In reality, physical RAM is limited and shared by dozens of processes. The OS creates this illusion through virtual memory, a system that translates virtual addresses into physical ones and seamlessly moves data between RAM and disk.
Virtual memory is why your laptop can run a browser with 50 tabs, a code editor, Spotify, and Slack simultaneously without any of them knowing the others exist. It is also why your system slows to a crawl when you open too many applications: the OS starts swapping pages to disk, and disk is thousands of times slower than RAM.
Define Terms
Visual Model
The full process at a glance. Click Start tour to walk through each step.
Virtual address translation: the page table maps virtual pages to physical frames, with page faults when data is on disk.
Code Example
// Simulating virtual memory with page table translation
function createPageTable(numPages) {
// -1 means page is on disk (not in RAM)
return Array.from({ length: numPages }, () => ({
frame: -1,
valid: false,
lastAccessed: 0
}));
}
function accessPage(pageTable, pageNum, physicalFrames, time) {
const entry = pageTable[pageNum];
if (entry.valid) {
// Page HIT: already in RAM
entry.lastAccessed = time;
console.log("Page " + pageNum + " -> Frame " + entry.frame + " (HIT)");
return { hit: true, frame: entry.frame };
}
// Page FAULT: must load from disk
console.log("Page " + pageNum + " -> PAGE FAULT!");
// Find free frame or evict (LRU)
let frame = physicalFrames.indexOf(null);
if (frame === -1) {
// No free frames: evict least recently used
let lruPage = -1;
let lruTime = Infinity;
for (let i = 0; i < pageTable.length; i++) {
if (pageTable[i].valid && pageTable[i].lastAccessed < lruTime) {
lruTime = pageTable[i].lastAccessed;
lruPage = i;
}
}
frame = pageTable[lruPage].frame;
pageTable[lruPage].valid = false;
console.log(" Evicted page " + lruPage + " from frame " + frame);
}
// Load page into frame
physicalFrames[frame] = pageNum;
entry.frame = frame;
entry.valid = true;
entry.lastAccessed = time;
console.log(" Loaded page " + pageNum + " into frame " + frame);
return { hit: false, frame: frame };
}
// 8 virtual pages, only 3 physical frames
const pageTable = createPageTable(8);
const frames = [null, null, null];
// Access sequence
[0, 1, 2, 3, 0, 1, 4, 0].forEach((page, t) => {
accessPage(pageTable, page, frames, t);
});Interactive Experiment
Try these exercises:
- Change the number of physical frames from 3 to 4 and re-run the access sequence. Count how many page faults occur now vs before.
- Try the access sequence
[0, 1, 2, 3, 0, 1, 2, 3]with 3 frames. Notice how you get a page fault on every access (this is called thrashing). - Add a counter to track the total number of hits vs faults. What is the hit ratio? How does increasing frames improve it?
Quick Quiz
Coding Challenge
Write a function called `lruPageReplacement` that takes an array of page access requests and the number of physical frames. Simulate LRU (Least Recently Used) page replacement and return the total number of page faults. When a page fault occurs and all frames are full, evict the page that was accessed least recently.
Real-World Usage
Virtual memory is foundational to modern computing:
- Process isolation prevents a bug in Chrome from corrupting your text editor's memory. Each process has its own virtual address space, enforced by the hardware MMU.
- Memory-mapped files let you treat a file on disk as if it were in memory. Databases like SQLite and LMDB use
mmap()for efficient file access without explicit read/write calls. - Copy-on-write (COW) is an optimization where forked processes share physical pages until one writes. This makes
fork()nearly free, even for processes using gigabytes of virtual memory. - Docker containers use virtual memory and namespaces to isolate processes. Each container sees its own memory space despite sharing the host's physical RAM.