Implementation of LRU Page Replacement Algorithm in C++

In this post, we implement the LRU (Least Recently Used) Page Replacement Algorithm in C++. When a page fault occurs and all frames are occupied, LRU evicts the page that has not been used for the longest time. Unlike FIFO which only tracks load order, LRU tracks usage history, making it significantly more effective in practice.

What is LRU Page Replacement?

LRU exploits the principle of temporal locality — recently used pages are likely to be used again soon. By evicting the page that hasn’t been touched for the longest time, LRU keeps the most actively used pages in memory.

  • Page Hit — Referenced page is already in a frame (its last-used time is implicitly updated).
  • Page Fault — Referenced page is not in memory. On a fault with full frames, the frame whose page was accessed least recently is selected for replacement.
  • LRU identification — For each frame, scan the page history backwards from the current reference to find the most recent access of that frame’s page. The frame with the oldest last-access is the LRU victim.

LRU does not suffer from Bélady’s Anomaly (unlike FIFO) and closely approximates the optimal (OPT) algorithm.


C++ Code Implementation

// ============================================================
// LRU (Least Recently Used) Page Replacement Algorithm in C++
// Evicts the page that was accessed least recently on a page fault
// ============================================================

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

int main() {

    int frameCount, pageRefCount;

    // -------------------------------------------------------
    // Step 1: Read frame size and page reference string
    // -------------------------------------------------------
    cout << "\n Enter Frame Size (number of frames in memory): ";
    cin  >> frameCount;

    cout << "\n Enter Number of Pages in the reference string: ";
    cin  >> pageRefCount;

    vector<int> pageReferenceString(pageRefCount);
    vector<int> frameContents(frameCount, -1);   // -1 = empty frame

    cout << "\n Enter the Page Reference String: ";
    for (int i = 0; i < pageRefCount; i++) cin >> pageReferenceString[i];

    int hitCount  = 0;
    int faultCount = 0;

    // -------------------------------------------------------
    // Step 2: Process each page reference
    // -------------------------------------------------------
    for (int refIndex = 0; refIndex < pageRefCount; refIndex++) {
        int currentPage = pageReferenceString[refIndex];

        // --- Check if page is already in a frame (Hit) ---
        bool isHit = false;
        for (int frameIdx = 0; frameIdx < frameCount; frameIdx++) {
            if (frameContents[frameIdx] == currentPage) {
                isHit = true;
                break;
            }
        }

        if (isHit) {
            hitCount++;
            cout << "\n Page " << currentPage << " [HIT]  | Frames: ";
        } else {
            faultCount++;

            // --- Page Fault: try to place in an empty frame first ---
            bool placedInEmptyFrame = false;
            for (int frameIdx = 0; frameIdx < frameCount; frameIdx++) {
                if (frameContents[frameIdx] == -1) {
                    frameContents[frameIdx] = currentPage;  // Load into empty frame
                    placedInEmptyFrame = true;
                    break;
                }
            }

            // --- If no empty frame, apply LRU replacement ---
            if (!placedInEmptyFrame) {
                // For each frame, find the most recent index in the page history
                // (before the current reference) where that frame's page was used.
                // The frame with the smallest (oldest) last-use index is the LRU victim.
                vector<int> lastUsedIndex(frameCount, -1);

                for (int frameIdx = 0; frameIdx < frameCount; frameIdx++) {
                    // Scan backwards from refIndex-1 to find last use of frameContents[frameIdx]
                    for (int pastRef = refIndex - 1; pastRef >= 0; pastRef--) {
                        if (pageReferenceString[pastRef] == frameContents[frameIdx]) {
                            lastUsedIndex[frameIdx] = pastRef;  // Most recent past use found
                            break;
                        }
                    }
                    // If never found, lastUsedIndex[frameIdx] remains -1
                    // (never used in the past = definitely the LRU victim)
                }

                // Find the frame with the smallest last-use index (least recently used)
                int lruFrameIdx = 0;
                for (int frameIdx = 1; frameIdx < frameCount; frameIdx++) {
                    if (lastUsedIndex[frameIdx] < lastUsedIndex[lruFrameIdx]) {
                        lruFrameIdx = frameIdx;  // This frame is even less recently used
                    }
                }

                frameContents[lruFrameIdx] = currentPage;  // Replace LRU page with new page
            }

            cout << "\n Page " << currentPage << " [MISS] | Frames: ";
        }

        // Display current frame contents after this reference
        for (int frameIdx = 0; frameIdx < frameCount; frameIdx++) {
            if (frameContents[frameIdx] == -1) cout << "- ";
            else cout << frameContents[frameIdx] << " ";
        }
    }

    // -------------------------------------------------------
    // Step 3: Summary statistics
    // -------------------------------------------------------
    cout << "\n\n ------- Summary -------";
    cout << "\n Total Page References : " << pageRefCount;
    cout << "\n Page Hits             : " << hitCount;
    cout << "\n Page Faults (Misses)  : " << faultCount;
    cout << "\n Hit Ratio             : " << (float)hitCount  / pageRefCount * 100 << "%";
    cout << "\n Fault Ratio           : " << (float)faultCount/ pageRefCount * 100 << "%";
    cout << endl;

    return 0;
}

Explanation of the Code

  1. Hit detection — Scans frameContents[] for the current page. A hit requires no frame change.
  2. Empty frame check — On a miss, empty frames (value = -1) are filled first before eviction logic runs.
  3. LRU victim selection — For each frame, the code scans pageReferenceString[0..refIndex-1] backwards to find the most recent time that frame’s page was used, stored in lastUsedIndex[frameIdx]. If a page was never used before, its index stays -1 — making it the immediate LRU candidate. The frame with the smallest value in lastUsedIndex[] is the LRU victim.
  4. Variable namingrefIndex (current position in reference string), pastRef (backward scan variable), lruFrameIdx (frame to evict), lastUsedIndex[] (per-frame last-access tracking) — all clearly named for readability.

Sample Output

 Enter Frame Size (number of frames in memory): 3
 Enter Number of Pages in the reference string: 20
 Enter the Page Reference String: 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1

 Page 7  [MISS] | Frames: 7  -  -
 Page 0  [MISS] | Frames: 7  0  -
 Page 1  [MISS] | Frames: 7  0  1
 Page 2  [MISS] | Frames: 2  0  1       <-- page 7 evicted (last used at step 0 — LRU)
 Page 0  [HIT]  | Frames: 2  0  1
 Page 3  [MISS] | Frames: 2  0  3       <-- page 1 evicted (last used at step 2 — LRU)
 Page 0  [HIT]  | Frames: 2  0  3
 Page 4  [MISS] | Frames: 4  0  3       <-- page 2 evicted (last used at step 3 — LRU)
 Page 2  [MISS] | Frames: 4  0  2       <-- page 3 evicted
 Page 3  [MISS] | Frames: 4  3  2       <-- page 0 evicted
 Page 0  [MISS] | Frames: 0  3  2       <-- page 4 evicted
 Page 3  [HIT]  | Frames: 0  3  2
 Page 2  [HIT]  | Frames: 0  3  2
 Page 1  [MISS] | Frames: 0  1  2       <-- page 3 evicted
 Page 2  [HIT]  | Frames: 0  1  2
 Page 0  [HIT]  | Frames: 0  1  2
 Page 1  [HIT]  | Frames: 0  1  2
 Page 7  [MISS] | Frames: 0  1  7       <-- page 2 evicted
 Page 0  [HIT]  | Frames: 0  1  7
 Page 1  [HIT]  | Frames: 0  1  7

 ------- Summary -------
 Total Page References : 20
 Page Hits             : 8
 Page Faults (Misses)  : 12
 Hit Ratio             : 40%
 Fault Ratio           : 60%

Step-by-Step Explanation of Input/Output

Reference string: 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1 | Frame size: 3

  • Steps 1–3 (pages 7, 0, 1): Compulsory misses — frames empty. Loaded into frames: {7, 0, 1}.
  • Step 4 (page 2): All frames full. Checking last use: page 7 at step 0, page 0 at step 1, page 1 at step 2. Page 7 is LRU → evicted. Frames: {2, 0, 1}.
  • Step 5 (page 0): Hit — page 0 is in frame. No change.
  • Step 6 (page 3): Miss. Page 1 was last used at step 2, page 2 at step 3, page 0 at step 4. Page 1 is LRU → evicted. Frames: {2, 0, 3}.
  • Steps 12–13, 15–17: Multiple hits as frequently used pages 0, 1, 2, 3 stay in memory.
  • Hit ratio 40% (8/20) vs FIFO’s 25% on the same string — LRU outperforms FIFO by keeping recently active pages in frames longer.

See Also

Bottom line is…

LRU is one of the best-performing practical page replacement algorithms because it leverages temporal locality to keep the most recently accessed pages in memory. While the theoretically optimal OPT algorithm has a lower fault rate, it requires future knowledge of page accesses — impossible at runtime. Real OS kernels use LRU approximations like the Clock algorithm (also called the Second Chance algorithm) to achieve near-LRU performance with much lower overhead. LRU itself is immune to Bélady’s Anomaly.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.