Skip to content
Memory Management Explained — Paging, Segmentation & Virtual Memory

Memory Management Explained — Paging, Segmentation & Virtual Memory

DodaTech Updated Jun 15, 2026 6 min read

Memory management is how an operating system allocates, tracks, and reclaims memory — ensuring every process gets the memory it needs without interfering with others.

What You’ll Learn

In this tutorial, you’ll learn how paging and segmentation work, how virtual memory gives each process its own address space, and how page replacement algorithms decide which pages to evict.

Why It Matters

Understanding memory management helps you write memory-efficient code, diagnose out-of-memory errors, and optimize performance. It’s also one of the most frequently tested topics in system design interviews.

Real-World Use

When you open a large file in a photo editor, the OS may only load parts into physical RAM — the rest stays on disk. If you scroll to an unloaded area, a page fault triggers the OS to load that part. Durga Antivirus Pro uses memory scanning to detect malware that tries to hide in other processes’ address space.


graph TD
  subgraph Virtual Address Space
    A[Code] --> B[Heap]
    B --> C[Stack]
    C --> D[Shared Libraries]
  end
  subgraph Physical Memory
    E[Page Frame 0]
    F[Page Frame 1]
    G[Page Frame 2]
    H[Page Frame 3]
  end
  subgraph Page Table
    I[VPN 0 -> PFN 2]
    J[VPN 1 -> PFN 0]
    K[VPN 2 -> Disk]
    L[VPN 3 -> PFN 1]
  end
  A --> I
  B --> J
  C --> K
  D --> L
  I --> G
  J --> E
  L --> F

Paging

Paging divides memory into fixed-size blocks called pages (virtual memory) and page frames (physical memory). A page table maps virtual page numbers to physical frame numbers.

When a program accesses address 0x1000, the MMU (Memory Management Unit) splits it into a page number and offset, looks up the page table, and finds the physical frame.

class MMU:
    def __init__(self, page_size=4096, physical_frames=8):
        self.page_size = page_size
        self.physical_frames = physical_frames
        self.page_table = {}
        self.physical_memory = [None] * physical_frames
        self.next_frame = 0

    def load_page(self, virtual_page, data="Data"):
        if self.next_frame >= self.physical_frames:
            print("OUT OF PHYSICAL MEMORY!")
            return False
        frame = self.next_frame
        self.physical_memory[frame] = data
        self.page_table[virtual_page] = frame
        self.next_frame += 1
        print(f"VPN {virtual_page} -> PFN {frame}")
        return True

    def access(self, virtual_address):
        page = virtual_address // self.page_size
        offset = virtual_address % self.page_size
        if page not in self.page_table:
            print(f"PAGE FAULT! VPN {page} not in memory")
            return None
        frame = self.page_table[page]
        print(f"VA 0x{virtual_address:04x} -> VPN {page}+{offset} -> PFN {frame}")
        return frame

mmu = MMU()
mmu.load_page(0)
mmu.load_page(1)
mmu.access(0x0000)
mmu.access(0x1004)
mmu.access(0x5000)

Expected output:

VPN 0 -> PFN 0
VPN 1 -> PFN 1
VA 0x0000 -> VPN 0+0 -> PFN 0
VA 0x1004 -> VPN 1+4 -> PFN 1
PAGE FAULT! VPN 5 not in memory

Virtual Memory

Virtual memory lets a process use more memory than physically available. Inactive pages are moved to disk (swap space). When the process accesses a swapped-out page, a page fault occurs and the OS loads it back.

Page Replacement Algorithms

When physical memory is full, the OS must evict a page to make room for a new one. The goal is to evict the page least likely to be used again.

FIFO (First-In, First-Out)

Evicts the page that was loaded first. Simple but can evict frequently used pages.

def fifo_page_faults(pages, frames):
    memory = []
    faults = 0
    for page in pages:
        if page not in memory:
            faults += 1
            if len(memory) < frames:
                memory.append(page)
            else:
                evicted = memory.pop(0)
                print(f"FIFO: Evict {evicted}, Load {page}")
                memory.append(page)
        else:
            print(f"FIFO: Hit {page}")
    print(f"Total faults: {faults}")
    return faults

fifo_page_faults([7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2], 3)

Expected output:

FIFO: Evict 7, Load 2
FIFO: Hit 0
FIFO: Evict 0, Load 3
FIFO: Hit 0
...

Total faults: 9

LRU (Least Recently Used)

Evicts the page that hasn’t been accessed for the longest time. Better than FIFO but requires tracking access history.

def lru_page_faults(pages, frames):
    memory = []
    faults = 0
    for i, page in enumerate(pages):
        if page not in memory:
            faults += 1
            if len(memory) < frames:
                memory.append(page)
            else:
                evicted = memory.pop(0)
                print(f"LRU: Evict {evicted}, Load {page}")
                memory.append(page)
        else:
            memory.remove(page)
            memory.append(page)
    print(f"LRU faults: {faults}")
    return faults

lru_page_faults([7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2], 3)

Expected output:

LRU: Evict 7, Load 2
...
Total faults: 7

Optimal (MIN / Belady’s Algorithm)

Evicts the page that will be used farthest in the future. This is the theoretical best — used as a benchmark.

AlgorithmFaults (ref string, 3 frames)
FIFO9
LRU7
Optimal6

Segmentation

Segmentation divides memory into variable-sized segments based on logical divisions (code, data, stack). Each segment has a base address and limit. Unlike paging, segments correspond to the programmer’s view of memory.

The Translation Lookaside Buffer (TLB)

The TLB is a hardware cache for page table entries. Without it, every memory access would require two RAM accesses: one to the page table, one to the data. The TLB caches recent translations, making most accesses single-cycle.

Common Mistakes

  1. Confusing paging and segmentation: Paging uses fixed-size blocks; segmentation uses variable-sized logical units. Modern systems use both (segmented paging).
  2. Ignoring Belady’s anomaly: FIFO can have MORE page faults with MORE frames — counterintuitive but true.
  3. Assuming virtual memory is infinite: Swap space is limited. Running out causes the OOM killer to terminate processes.
  4. Forgetting TLB misses: A TLB miss requires a page table walk, which can take 10-100 cycles.
  5. Using large page sizes blindly: Huge pages (2 MB, 1 GB) reduce TLB pressure but waste memory (internal fragmentation).

Practice Questions

  1. What is a page fault? When a program accesses a virtual page that isn’t mapped to physical memory. The OS must load it from disk.

  2. Why is LRU better than FIFO? LRU considers actual access patterns, evicting pages not used recently. FIFO may evict a frequently used page that happened to be loaded early.

  3. What does the TLB do? Caches recent virtual-to-physical address translations, avoiding slow page table lookups in RAM.

  4. What is thrashing? When the system spends more time swapping pages in and out than doing actual work — the working set doesn’t fit in memory.

  5. How does a dirty page differ from a clean page? A dirty page has been modified and must be written to disk before eviction. A clean page can be discarded.

Challenge

Write a simulation that compares FIFO, LRU, and Optimal for 100 random page references with frame sizes from 2 to 10. Graph the results.

Real-World Task

Check your system’s swap usage (swapon -s on Linux, Task Manager on Windows). How much swap is being used? Which processes would be affected if swap runs out?

Mini Project: Page Fault Analyzer

Build a Python program that generates random page reference strings and visualizes page faults for different algorithms. Include Belady’s anomaly demonstration.

Security angle: Memory scanning tools in Durga Antivirus Pro use page-level access monitoring to detect processes attempting unauthorized memory access — the hardware MMU provides this isolation.

What’s Next

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

What’s Next

Congratulations on completing this Memory Management tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro