Memory Management Explained — Paging, Segmentation & Virtual Memory
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 memoryVirtual 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: 9LRU (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: 7Optimal (MIN / Belady’s Algorithm)
Evicts the page that will be used farthest in the future. This is the theoretical best — used as a benchmark.
| Algorithm | Faults (ref string, 3 frames) |
|---|---|
| FIFO | 9 |
| LRU | 7 |
| Optimal | 6 |
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
- Confusing paging and segmentation: Paging uses fixed-size blocks; segmentation uses variable-sized logical units. Modern systems use both (segmented paging).
- Ignoring Belady’s anomaly: FIFO can have MORE page faults with MORE frames — counterintuitive but true.
- Assuming virtual memory is infinite: Swap space is limited. Running out causes the OOM killer to terminate processes.
- Forgetting TLB misses: A TLB miss requires a page table walk, which can take 10-100 cycles.
- Using large page sizes blindly: Huge pages (2 MB, 1 GB) reduce TLB pressure but waste memory (internal fragmentation).
Practice Questions
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.
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.
What does the TLB do? Caches recent virtual-to-physical address translations, avoiding slow page table lookups in RAM.
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.
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