Physical memory is finite; the virtual memory processes ask for is not. When every frame is full and a process faults on a page that must be brought in, the operating system faces the defining decision of virtual memory: which resident page do we evict to make room? Guess well and the victim is a page nobody wanted again — the fault is nearly free. Guess badly and you evict a page that is needed one instruction later, forcing it right back in. Over billions of references, that policy is worth enormous performance.
This is a pure policy question — the
Start with the impossible ideal. Bélády's OPT algorithm evicts the page that will not be used for the longest time in the future. It is provably optimal — no algorithm can produce fewer faults on a given reference string. There is just one problem: it requires knowing the future. OPT cannot be implemented for real workloads; its value is as a yardstick. When we measure a real algorithm we ask "how close to OPT did it get?", and the gap tells us how much a better predictor could possibly buy.
The simplest realisable policy: evict the page that has been resident longest, regardless of use — a queue. It needs no hardware help, just a list. But FIFO has no idea which pages are useful; it will happily evict a hot, constantly-used page simply because it arrived early. Worse, FIFO suffers Bélády's anomaly: giving it more memory can produce more faults — a violation of the obvious intuition that more RAM should never hurt. The runnable comparison below includes the famous anomaly-triggering reference string.
Run the reference string
Least Recently Used evicts the page unused for the longest time in the past,
betting that recency predicts the future — and for real programs, thanks to
The way out is the two status bits the hardware does give us. The MMU sets a PTE's accessed bit whenever the page is touched and its dirty bit whenever it is written. The OS cannot see the exact order of accesses, but it can periodically read and clear the accessed bit to learn "was this page used since I last looked?" That single bit of recent-use information is enough to build a cheap LRU approximation.
CLOCK (second-chance) is the workhorse. Arrange the frames in a circle with a moving hand. To find a victim, look at the page under the hand:
A page that keeps getting used keeps getting its bit re-set before the hand returns, so it survives; a page that falls out of use eventually meets the hand with a 0 bit and is evicted. CLOCK gets most of LRU's benefit for almost none of its cost — one bit and a circular scan. Real kernels refine it: Linux keeps two LRU-ish lists (active/inactive) and factors in the dirty bit (evicting a clean page is cheaper — no write-back needed). Compare all four policies on the same reference string:
Plain LRU has a famous weakness: a single sequential scan of a huge file (a backup, a
| Algorithm | Key idea | Guards against | Seen in |
|---|---|---|---|
| 2Q | a probationary queue for one-hit pages; promote only on a second hit | scan pollution | PostgreSQL-style buffer pools |
| ARC | self-tunes the balance of recency vs frequency using ghost lists | changing workloads | ZFS, IBM storage |
| LIRS | ranks by reuse distance (inter-reference recency) | weak-locality scans | research, some DB caches |
The common thread: a page touched once should not have the same standing as a page touched often. ARC (Adaptive Replacement Cache) is the star — it keeps "ghost" entries recording pages it recently evicted, and if it sees a ghost come back it shifts its own recency-versus-frequency balance automatically. It is why a ZFS box stays fast across wildly different workloads without hand-tuning.
A frequent error is to treat the hardware accessed bit as if it were a timestamp or a use-counter. It is neither — it is a single bit that means "touched at least once since I last cleared it." You cannot read an access order out of it, and you cannot tell a page touched once from a page touched a million times. That coarseness is exactly why CLOCK only approximates LRU rather than implementing it, and why algorithms wanting frequency information (ARC, LIRS) have to build it up in software over time by sampling and clearing the bit repeatedly. Never claim your policy "is LRU" when it is really "second-chance driven by a one-bit sampled signal." The gap between them is the whole subject.
Notice what stayed constant through all of this. The mechanism — trap on fault, allocate a frame,
write back a dirty victim, fetch the wanted page, patch the page table, restart the instruction — never
changed. Only the policy (pick the victim) moved, from OPT's oracle to FIFO's queue to CLOCK's
one-bit approximation to ARC's self-tuning. That is the OS design pattern in its purest form: build the
expensive machinery once, and let a cheap, swappable decision ride on top. The next lesson,