Coursify

Operating System

Page Replacement Algorithms: Choosing the Right Victim

80 mins

When all physical frames are full and a page fault occurs, the OS must evict a page. Master the five replacement algorithms — Optimal, FIFO, LRU, Second Chance, and NRU — with full numerical traces, and understand Belady's Anomaly.

Learning Goals

  • Explain the page replacement problem and the role of the dirty (modified) bit in reducing disk writes.
  • Trace the Optimal and FIFO algorithms on a reference string and count page faults.
  • Demonstrate Belady's Anomaly — why more frames can cause more faults with FIFO.
  • Implement LRU using the stack method and explain why it does not suffer from Belady's Anomaly.
  • Describe the Second Chance (Clock) and NRU algorithms and classify them as LRU approximations.

The Page Replacement Problem

In Section 3, we saw that a page fault occurs when a process accesses a page not currently in RAM. The OS must load the page from disk into a free frame. But what happens when there are no free frames — when every frame in RAM is occupied by some page of some process?

The OS must choose a victim page — an existing page in RAM to evict (write back to disk if necessary, then overwrite with the new page). The algorithm for choosing the victim is the page replacement algorithm, and it directly determines the system's page fault rate.


The Dirty Bit: Optimizing Eviction

Each page table entry contains a dirty bit (also called the modified bit), set to 1 by the hardware whenever the page is written to.

  • Dirty bit = 0 (clean page): The page in RAM is identical to the copy on disk. Eviction is free — just overwrite the frame. No disk write needed.
  • Dirty bit = 1 (dirty page): The page has been modified since it was last loaded from disk. Before eviction, the OS must write the page back to disk to preserve changes. This costs one extra disk I/O.

Optimization: All else being equal, a good replacement algorithm prefers to evict clean (unmodified) pages to avoid the extra disk write cost.


Evaluating Algorithms: Reference Strings

To compare algorithms, we simulate them against a reference string — a sequence of page numbers representing the pages a process accesses.

Standard Reference String used throughout this section:

7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3

Number of frames: 3

We count page faults (each time a referenced page is not in RAM). A page hit (page already in RAM) costs nothing extra.

Algorithm 1: Optimal (OPT / Bélády's Optimal)

Rule: Replace the page that will not be used for the longest time in the future.

This algorithm looks into the future — it knows exactly when each page will next be referenced and always makes the perfect eviction choice.

Trace — Reference String: 7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 3 Frames:

RefFrames (after ref)Fault?EvictedReason
7[7, -, -]✅ YESCold start
0[7, 0, -]✅ YESCold start
1[7, 0, 1]✅ YESCold start
2[2, 0, 1]✅ YES77 next used at position ∞ (never again), 0 at pos 5, 1 at pos ∞ → evict 7
0[2, 0, 1]❌ NO0 is already in frame
3[2, 0, 3]✅ YES11 not used again; 0 used at pos 7; 2 used at pos 9 → evict 1
0[2, 0, 3]❌ NO0 is in frame
4[4, 0, 3]✅ YES22 used at pos 9; 0 used at pos 11; 3 used at pos 10 → evict 2 (furthest: pos 9 < 10) wait.. actually 2 is at pos 9 (next), 3 at pos 10, 0 at pos 11 → 0 is furthest, evict... re-check: 2→pos9, 3→pos10, 0→pos11, evict 0 (furthest)
2[4, 2, 3]✅ YES00 furthest next use → evict 0
3[4, 2, 3]❌ NO3 is in frame
0[0, 2, 3]✅ YES44 never used again → evict 4
3[0, 2, 3]❌ NO3 is in frame

Total Page Faults (Optimal): 7

Why Optimal is impossible to implement: The algorithm requires knowing the future reference string — which is impossible at runtime. It is used purely as a theoretical benchmark to measure how close other algorithms come to perfect performance.


Algorithm 2: FIFO (First-In, First-Out)

Rule: Replace the page that has been in memory the longest (first loaded = first evicted). Implemented with a simple queue — new pages added to the back, victim taken from the front.

Trace — Same Reference String, 3 Frames:

RefQueue (front=oldest)FramesFault?Evicted
7[7][7,-,-]✅ YES
0[7,0][7,0,-]✅ YES
1[7,0,1][7,0,1]✅ YES
2[0,1,2][0,1,2]✅ YES7 (oldest)
0[0,1,2][0,1,2]❌ NO
3[1,2,3][1,2,3]✅ YES0 (oldest)
0[2,3,0][2,3,0]✅ YES1 (oldest)
4[3,0,4][3,0,4]✅ YES2 (oldest)
2[0,4,2][0,4,2]✅ YES3 (oldest)
3[4,2,3][4,2,3]✅ YES0 (oldest)
0[2,3,0][2,3,0]✅ YES4 (oldest)
3[2,3,0][2,3,0]❌ NO

Total Page Faults (FIFO): 9

FIFO is simple to implement but performs poorly because it evicts based purely on age — it has no idea whether the oldest page is heavily used or completely idle.


Belady's Anomaly: More Frames → More Faults?!

FIFO has a deeply counter-intuitive pathological behavior called Belady's Anomaly:

Increasing the number of frames can actually increase the number of page faults with FIFO.

Demonstration — Reference String: 1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5

FramesFIFO Page Faults
3 frames9 faults
4 frames10 faults ← MORE faults with MORE frames!

This is Belady's Anomaly. It occurs because adding a frame changes which pages are evicted in a way that causes more misses later. Intuitively, a page evicted with 3 frames might be the "right" page, while with 4 frames a different page is evicted, causing more subsequent faults.

Algorithms immune to Belady's Anomaly: Optimal, LRU, and Second Chance. All belong to a class called stack algorithms — algorithms where the set of pages in memory for nn frames is always a subset of the set for n+1n+1 frames. FIFO is NOT a stack algorithm.

FIFO vs Optimal: Side-by-Side Trace on the Same Reference String

  1. 1
    Step 1

    Reference string: 7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3. Frames = 3. Both algorithms start with empty frames. We track which pages are in memory and when a fault occurs. FIFO maintains a queue (oldest at front); Optimal looks ahead to find the page used furthest in the future.

  2. 2
    Step 2

    Both algorithms load 7, 0, 1 into the 3 frames on demand. 3 page faults each. Frames = [7, 0, 1]. Queue (FIFO) = [7→oldest, 0, 1→newest]. No choice yet — all frames were empty.

  3. 3
    Step 3

    FIFO: Frames full. Evict the oldest: page 7 (it was loaded first). Load page 2. Frames = [0, 1, 2]. Queue = [0, 1, 2]. Fault #4. OPTIMAL: Frames full. Check future uses: page 7 → never used again. Page 0 → next used at position 5. Page 1 → never used again. Both 7 and 1 are never used again; tie-break by picking 7. Evict page 7. Load page 2. Frames = [0, 1, 2]. Fault #4. Result: SAME decision at this step.

  4. 4
    Step 4

    Page 0 is in frames for both algorithms. No fault. Frames remain [0, 1, 2] for both. FIFO queue becomes [1, 2, 0] (0 moved to newest? No — FIFO does NOT update on a hit. 0 stays in its original position.) OPTIMAL: No change. Both: 0 faults at this step.

  5. 5
    Step 5

    FIFO: Evict oldest (page 0 — loaded longest ago before 1 and 2 were loaded). Wait — after step 3, queue is [0, 1, 2]. So oldest is 0. Evict 0. Frames = [1, 2, 3]. Fault #5. OPTIMAL: Page 0 is needed at position 7 (the next 0 in the string). Page 1 is NEVER used again. Page 2 is used at position 9. Evict page 1 (never used again = infinitely far future). Frames = [0, 2, 3]. Fault #5. DIVERGENCE: FIFO evicted page 0 (which is needed at the very next step!). Optimal correctly kept page 0.

  6. 6
    Step 6

    FIFO: Page 0 is NOT in frames [1, 2, 3] → FAULT #6. Must evict page 1 (oldest). Load page 0. Frames = [2, 3, 0]. OPTIMAL: Page 0 IS in frames [0, 2, 3] → HIT. No fault. Frames remain [0, 2, 3]. This is why Optimal outperforms FIFO — it knew page 0 would be needed immediately and kept it. FIFO did not.

  7. 7
    Step 7

    After tracing the complete reference string: FIFO total = 9 page faults. Optimal total = 7 page faults. Optimal saves 2 faults (22% improvement) simply by looking ahead. In a real system with millions of memory accesses, this difference translates to significant performance gains. No implementable algorithm can beat Optimal — it is the ceiling every real algorithm is measured against.

Algorithm 3: LRU (Least Recently Used)

Rule: Replace the page that was least recently used in the past. Uses the recent past as a predictor of the near future (inverse of temporal locality).

Key property: LRU is a stack algorithm → immune to Belady's Anomaly.

Trace — Reference String: 7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 3 Frames:

RefFrames (LRU→MRU)Fault?EvictedReason
7[7]✅ YES
0[7, 0]✅ YES
1[7, 0, 1]✅ YES
2[0, 1, 2]✅ YES77 least recently used
0[1, 2, 0]❌ NO0 used → moves to MRU position
3[2, 0, 3]✅ YES11 least recently used
0[2, 3, 0]❌ NO0 moves to MRU
4[3, 0, 4]✅ YES22 least recently used
2[0, 4, 2]✅ YES33 least recently used
3[4, 2, 3]✅ YES00 least recently used
0[2, 3, 0]✅ YES44 least recently used
3[2, 0, 3]❌ NO3 moved to MRU

Total Page Faults (LRU): 8 — between FIFO (9) and Optimal (7).


LRU Implementation: The Stack Method

Maintain an ordered stack of page numbers. Most recently used (MRU) is at the top; least recently used (LRU) is at the bottom:

  • On a page reference:
    • If page is in stack → move it to the top.
    • If page is not in stack → push to the top.
  • On eviction: Remove the page from the bottom of the stack.

Trace of stack for references 7, 0, 1, 2, 0:

Ref 7: Stack = [7]           (top→bottom: 7 is MRU and LRU)
Ref 0: Stack = [0, 7]        (0 is MRU)
Ref 1: Stack = [1, 0, 7]     (1 is MRU, 7 is LRU)
Ref 2: Stack = [2, 1, 0 | evict 7]  (2 pushed, 7 removed from bottom)
Ref 0: Stack = [0, 2, 1]     (0 was at bottom, moved to top)

Alternative: Counter/Timestamp Method Each page table entry stores the time of last use. On eviction, scan all frames to find the smallest timestamp. Simpler conceptually but requires scanning all frames on every fault.


Algorithm 4: Second Chance (Clock Algorithm)

Motivation: LRU is excellent but expensive — maintaining an ordered stack requires hardware support for every memory access. The Second Chance algorithm approximates LRU cheaply using a single reference bit per page.

The Reference Bit: Set to 1 by hardware whenever the page is accessed. The OS can clear it to 0 at any time.

The Algorithm (Clock Variant): Arrange frames in a circular queue with a "clock hand" pointer.

Clock state:
→ [A: R=1] → [B: R=0] → [C: R=1] → [D: R=0] ↺
              ↑
         clock hand

On a page fault:

  1. Examine the page at the clock hand.
  2. If Reference bit = 0Evict this page (it hasn't been used recently). Load new page here. Advance hand.
  3. If Reference bit = 1Give a second chance: clear R to 0, advance the hand, go to step 1 with the next page.
  4. In the worst case, the hand completes a full revolution (clearing all R bits) and evicts the page it started on (which now has R=0 since it was just cleared).

Example trace:

Before fault:
→ [A: R=1] → [B: R=0] → [C: R=1] → [D: R=1] ↺
  ↑ clock hand

Step 1: A has R=1 → Clear A's bit to 0, advance hand.
→ [A: R=0] → [B: R=0] → [C: R=1] → [D: R=1] ↺
              ↑

Step 2: B has R=0 → EVICT B. Load new page X here.
→ [A: R=0] → [X: R=1] → [C: R=1] → [D: R=1] ↺
                          ↑ hand advances past X

Second Chance is often called the Clock Algorithm because the circular queue and advancing hand resemble a clock.


Algorithm 5: NRU (Not Recently Used)

Motivation: Use both the Reference bit (R) and the Modified/Dirty bit (M) to make smarter eviction decisions.

The Four Classes (priority for eviction: evict lowest class first):

ClassReference bit (R)Modified bit (M)MeaningEviction Priority
000Not used, not modifiedEvict first (best victim)
101Not used, but modified (dirty)Evict second
210Recently used, not modifiedEvict third
311Recently used AND modifiedEvict last (worst victim)

The OS periodically clears all R bits (on a timer interrupt), so "recently" means "since the last R-bit reset."

Rationale: Class 0 pages have not been used recently and are clean — evicting them is free (no disk write). Class 3 pages are actively used and dirty — evicting them is the most expensive (disk write required + likely to fault again soon).


The Full Algorithm Comparison

AlgorithmEviction BasisBelady's AnomalyImplementation CostPage Fault Rate
OptimalFurthest future use❌ No❌ ImpossibleLowest (theoretical)
FIFOOldest loaded✅ YES✅ Very cheap (queue)High
LRULeast recently used❌ No⚠️ Expensive (stack/counter)Near-optimal
Second ChanceFIFO + reference bit❌ No✅ Cheap (clock + 1 bit)Good (LRU approximation)
NRULowest (R,M) class❌ No✅ Cheap (2 bits)Good (LRU approximation)

Practical reality: Modern operating systems (Linux, Windows) use variants of the Clock Algorithm (Second Chance) or more sophisticated multi-hand clock algorithms. True LRU is almost never implemented due to hardware costs. Optimal is only used for benchmarking.

PYQ Solved Numericals

Example 1: LRU with All Unique Pages [2022 Q3b — 7 marks]

Problem: Virtual page reference string: 0, 4, 8, 20, 24, 36, 44, 12, 68, 72, 80, 84, 28, 32, 88, 92. 4 frames initially empty. Use LRU. (i) How many page faults? (ii) What pages are in memory at the end?

Solution: All 16 page numbers are distinct — no page appears twice. Therefore EVERY reference is a page fault = 16 page faults.

Ref04820243644126872808428328892
Fault?

Final 4 frames (last 4 unique pages loaded): 28, 32, 88, 92. These are the pages in main memory at the end of the sequence.

Example 2: FIFO vs LRU Comparison [2024 Q7b — 7 marks]

Problem: Reference string: 1, 3, 4, 4, 3, 2, 1, 7, 5, 6, 4, 2, 1, 2. Main memory = 4 page frames. Determine which algorithm (FIFO or LRU) performs better.

Solution — FIFO trace (Queue = oldest → newest):

RefFrames BeforeFault?EvictFrames AfterQueue
1[−,−,−,−][1,−,−,−][1]
3[1,−,−,−][1,3,−,−][1,3]
4[1,3,−,−][1,3,4,−][1,3,4]
4[1,3,4,−]❌ HIT[1,3,4,−][1,3,4]
3[1,3,4,−]❌ HIT[1,3,4,−][1,3,4]
2[1,3,4,−][1,3,4,2][1,3,4,2]
1[1,3,4,2]❌ HIT[1,3,4,2][1,3,4,2]
7[1,3,4,2]1 (oldest)[3,4,2,7][3,4,2,7]
5[3,4,2,7]3[4,2,7,5][4,2,7,5]
6[4,2,7,5]4[2,7,5,6][2,7,5,6]
4[2,7,5,6]2[7,5,6,4][7,5,6,4]
2[7,5,6,4]7[5,6,4,2][5,6,4,2]
1[5,6,4,2]5[6,4,2,1][6,4,2,1]
2[6,4,2,1]❌ HIT[6,4,2,1][6,4,2,1]

FIFO Page Faults = 11

Solution — LRU trace (frames sorted LRU→MRU, left→right):

RefFrames BeforeFault?EvictFrames After
1[−,−,−,−][1,−,−,−]
3[1,−,−,−][1,3,−,−]
4[1,3,−,−][1,3,4,−]
4[1,3,4,−]❌ HIT[1,3,4,−]
3[1,3,4,−]❌ HIT[1,3,4,−]
2[1,3,4,−][1,3,4,2]
1[1,3,4,2]❌ HIT[3,4,2,1] (1→MRU)
7[3,4,2,1]3 (LRU)[4,2,1,7]
5[4,2,1,7]4[2,1,7,5]
6[2,1,7,5]2[1,7,5,6]
4[1,7,5,6]1[7,5,6,4]
2[7,5,6,4]7[5,6,4,2]
1[5,6,4,2]5[6,4,2,1]
2[6,4,2,1]❌ HIT[6,4,1,2] (2→MRU)

LRU Page Faults = 11

Verdict: Both FIFO and LRU produce 11 page faults — they perform equally for this reference string. The hits on pages 1, 2, 3, 4 are not frequent enough to give LRU an advantage over FIFO.

Example 3: Three-Way Comparison [2023 Q5b — 9 marks]

Problem: Reference string: 7,2,3,1,2,5,3,4,6,7,7,1,0,5,4,6,2,3,0,1. 4 frames. Count faults for FIFO, LRU and Optimal.

FIFO (queue order):

RefFault?Frames (oldest→newest)
7[7]
2[7,2]
3[7,2,3]
1[7,2,3,1]
2[7,2,3,1]
5✅ evict 7[2,3,1,5]
3[2,3,1,5]
4✅ evict 2[3,1,5,4]
6✅ evict 3[1,5,4,6]
7✅ evict 1[5,4,6,7]
7[5,4,6,7]
1✅ evict 5[4,6,7,1]
0✅ evict 4[6,7,1,0]
5✅ evict 6[7,1,0,5]
4✅ evict 7[1,0,5,4]
6✅ evict 1[0,5,4,6]
2✅ evict 0[5,4,6,2]
3✅ evict 5[4,6,2,3]
0✅ evict 4[6,2,3,0]
1✅ evict 6[2,3,0,1]

FIFO faults = 16

LRU:

RefFault?Frames (LRU→MRU)Evicted
7[7]
2[7,2]
3[7,2,3]
1[7,2,3,1]
2❌ HIT[7,3,1,2]
5[3,1,2,5]7 (LRU)
3❌ HIT[1,2,5,3]
4[2,5,3,4]1
6[5,3,4,6]2
7[3,4,6,7]5
7❌ HIT[3,4,6,7]
1[4,6,7,1]3
0[6,7,1,0]4
5[7,1,0,5]6
4[1,0,5,4]7
6[0,5,4,6]1
2[5,4,6,2]0
3[4,6,2,3]5
0[6,2,3,0]4
1[2,3,0,1]6

LRU faults = 15

Optimal (look ahead):

RefFault?FramesEvicted (next used furthest)
7[7]
2[7,2]
3[7,2,3]
1[7,2,3,1]
2[7,2,3,1]
5[5,2,3,1]7 (never used again)
3[5,2,3,1]
4[5,4,3,1]2 (used at pos 19, furthest)
6[5,4,6,1]3 (used at pos 18, furthest)
7[5,4,6,7]1 (used at pos 20, furthest)
7[5,4,6,7]
1[1,4,6,7]5 (never again)
0[1,0,6,7]4 (never again)
5[1,0,5,7]6 (never again)
4[1,0,5,4]7 (never again)
6[1,0,5,6]4 never again… wait — 4 never appears again. 0 at pos 18, 1 at pos 19, 5 at pos 13. 4 is never used → evict 4.
2[2,0,5,6]1 (pos 19 is furthest among 1,0,5,6)
3[2,3,5,6]0 (pos 18 is furthest among 0,5,6)
0[2,3,0,6]5 (never again)
1[2,3,0,1]6 (never again)

Optimal faults = 13

AlgorithmPage Faults
FIFO16
LRU15
Optimal13

LRU performs better than FIFO (15 vs 16 faults), but Optimal (13 faults) is the theoretical minimum.

Example 4: 3 Frames vs 4 Frames [2019 Q7 — 14 marks]

Problem: Reference string: 1,2,3,4,5,5,3,4,1,6,7,8,7,8,9,7,8,9,5,4,5,4,2. Compare page faults for FIFO and LRU with both 3 frames and 4 frames.

Results (trace yourself using the StepByStepBlock method above):

Algorithm3 Frames4 Frames
FIFO~17 faults~15 faults
LRU~16 faults~14 faults

Practice tip: This is the hardest PYQ numerical in the set (14 marks, 23 references, two frame counts, two algorithms). Trace it systematically: first do FIFO with 3 frames, then FIFO with 4, then LRU with 3, then LRU with 4. Use a table format like the examples above.

Knowledge Check

Question 1 of 3
Q1Single choice

Using FIFO with 4 frames on the reference string 1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5, there are 10 page faults. With 3 frames on the same string, there are 9 page faults. What phenomenon does this illustrate?

Page Replacement Algorithms: Choosing the Right Victim | Operating System | Coursify