Facebook Pixel

Second Chance Page Replacement

In our previous look at page replacement, we saw that FIFO is incredibly fast to implement but performs poorly because it ignores whether a page is actually being used. LRU performs excellently but is very expensive to implement because it requires updating data structures on every single memory access.

The Second Chance Algorithm (often called the Clock Algorithm) is a brilliant compromise. It takes the blazing speed of the FIFO queue but adds a simple check to ensure it doesn't accidentally evict heavily used pages.

What is the Second Chance Algorithm?

The algorithm uses a standard FIFO queue, but it looks at the hardware "Reference Bit" of each page. When a page is first loaded or successfully accessed (a hit), the hardware sets its Reference Bit to 1.

When RAM is full and a page must be evicted, the OS looks at the oldest page in the queue:

1. If the Reference Bit is 0, the page hasn't been used recently. It is evicted immediately.

2. If the Reference Bit is 1, the page has been used recently. Instead of evicting it, the OS gives it a "second chance". It flips the bit to 0, moves the page to the back of the queue (treating it as newly arrived), and moves on to check the next page.

The Clock Analogy
This is almost always implemented as a circular list. Imagine a clock face where each number is a page frame. A "clock hand" sweeps around the circle. If it points to a page with a 1, it flips it to 0 and sweeps to the next page. It keeps sweeping until it finds a 0, evicts that page, inserts the new one, and advances one step forward.

Steps of the Algorithm

  • A page fault occurs and physical RAM is completely full.
  • The pointer (clock hand) examines the oldest page in the circular queue.
  • If the Reference Bit is 0: Evict the page. Put the new page in this frame, set its bit to 1, and advance the pointer.
  • If the Reference Bit is 1: Give it a second chance. Flip the bit to 0, leave the page in RAM, and advance the pointer to check the next oldest page.
  • Repeat this process until a page with a 0 bit is successfully found and evicted.

A Worked Example

Let's trace a simple string: 1, 2, 3, 1, 4 with 3 physical frames. We will represent frames as Page(Bit).

text
Step 1: Request 1. Fault. Load 1. Pointer points to frame 1.
Frames: [ 1(1), Empty, Empty ]

Step 2: Request 2. Fault. Load 2.
Frames: [ 1(1), 2(1), Empty ]

Step 3: Request 3. Fault. Load 3.
Frames: [ 1(1), 2(1), 3(1) ]

Step 4: Request 1. Hit! Page 1 is already in memory. Its bit is refreshed to 1 (it already was 1, so no change).
Frames: [ 1(1), 2(1), 3(1) ]

Step 5: Request 4. Fault! RAM is full. The pointer is at Page 1.
- Check Page 1: Bit is 1. Second chance! Flip bit to 0. Advance pointer to Page 2.
- Check Page 2: Bit is 1. Second chance! Flip bit to 0. Advance pointer to Page 3.
- Check Page 3: Bit is 1. Second chance! Flip bit to 0. Advance pointer back to Page 1.
- Check Page 1 again: Bit is now 0! 
- EVICT Page 1. Load Page 4. Set bit to 1. Advance pointer to Page 2.
Frames: [ 4(1), 2(0), 3(0) ]

Implementation in Code

Here is how you might simulate the Second Chance (Clock) algorithm using arrays to track the frames and their reference bits.

Python Implementation

python
def second_chance(reference_string, capacity):
    frames = [-1] * capacity
    ref_bits = [0] * capacity
    pointer = 0
    page_faults = 0
    
    for page in reference_string:
        # Check if page is already in memory (Hit)
        if page in frames:
            index = frames.index(page)
            ref_bits[index] = 1 # Refresh the bit
            continue
            
        # Page Fault: We need to load the page
        page_faults += 1
        
        # Find a victim using the clock hand (pointer)
        while True:
            # If bit is 0, we found our victim
            if ref_bits[pointer] == 0:
                frames[pointer] = page
                ref_bits[pointer] = 1 # New page gets bit 1
                pointer = (pointer + 1) % capacity # Advance pointer
                break
            # If bit is 1, give second chance
            else:
                ref_bits[pointer] = 0 # Flip to 0
                pointer = (pointer + 1) % capacity # Advance pointer
                
    return page_faults

# Test the example
pages = [1, 2, 3, 1, 4]
print(f"Page Faults: {second_chance(pages, 3)}")

C++ Implementation

cpp
#include <iostream>
#include <vector>
using namespace std;

int secondChance(vector<int> pages, int capacity) {
    vector<int> frames(capacity, -1);
    vector<int> refBits(capacity, 0);
    int pointer = 0;
    int faults = 0;

    for (int page : pages) {
        bool found = false;
        for (int i = 0; i < capacity; i++) {
            if (frames[i] == page) {
                refBits[i] = 1; // Hit! Refresh bit
                found = true;
                break;
            }
        }

        if (!found) {
            faults++;
            while (true) {
                if (refBits[pointer] == 0) {
                    frames[pointer] = page;
                    refBits[pointer] = 1;
                    pointer = (pointer + 1) % capacity;
                    break;
                } else {
                    refBits[pointer] = 0; // Second chance
                    pointer = (pointer + 1) % capacity;
                }
            }
        }
    }
    return faults;
}

Java Implementation

java
public class SecondChance {
    public static int run(int[] pages, int capacity) {
        int[] frames = new int[capacity];
        int[] refBits = new int[capacity];
        int pointer = 0, faults = 0;
        
        // Initialize frames to -1
        for (int i = 0; i < capacity; i++) frames[i] = -1;

        for (int page : pages) {
            boolean found = false;
            for (int i = 0; i < capacity; i++) {
                if (frames[i] == page) {
                    refBits[i] = 1; // Hit
                    found = true;
                    break;
                }
            }

            if (!found) {
                faults++;
                while (true) {
                    if (refBits[pointer] == 0) {
                        frames[pointer] = page;
                        refBits[pointer] = 1;
                        pointer = (pointer + 1) % capacity;
                        break;
                    } else {
                        refBits[pointer] = 0; // Give second chance
                        pointer = (pointer + 1) % capacity;
                    }
                }
            }
        }
        return faults;
    }
}

Summary

The Second Chance algorithm is a highly practical solution to the page replacement problem. By using a simple hardware bit, it avoids the massive software overhead of LRU linked lists while still successfully identifying and protecting frequently used pages from being evicted by a naive FIFO queue.

Understanding the Second Chance Logic

Question 1 of 1

Test your understanding of the core rule of the algorithm.

A page fault occurs and RAM is full. The eviction pointer examines the oldest page in the queue and sees that its Reference Bit is set to 1. What exactly does the OS do to this page?
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.