Memory management is a crucial part of any operating system. It decides how memory is allocated and deallocated to processes. There are different memory allocation strategies like:
-
First Fit
-
Best Fit
-
Worst Fit
In this tutorial, we’ll create a simple memory management simulator in C++ that uses these three strategies. We'll explain each step in easy words with code so you can understand and implement it on your own.
📌 What Is Memory Allocation in Operating Systems?
When programs run, they need memory (RAM) to store data and instructions. The Operating System (OS) decides how to divide this memory among multiple programs. If not done efficiently, some memory might be wasted or some programs might not run properly.
To solve this, different memory allocation strategies are used:
-
First Fit: Allocate the first memory block that is large enough.
-
Best Fit: Allocate the smallest memory block that is big enough (to reduce wastage).
-
Worst Fit: Allocate the largest memory block (to leave big chunks behind).
🧩 Let's Build the Simulator
We’ll simulate a memory of fixed size and allow the user to request memory for different processes. We'll apply each strategy and show how memory is allocated.
🛠️ Step 1: Include Required Libraries
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
🧱 Step 2: Define Structures
We’ll define a structure for memory blocks and processes.
struct Block {
int id;
int size;
bool allocated;
};
struct Process {
int id;
int size;
int blockAllocated;
};
🗃️ Step 3: Input Memory Blocks and Processes
void inputBlocks(vector<Block> &blocks, int n) {
for (int i = 0; i < n; i++) {
int size;
cout << "Enter size of block " << i + 1 << ": ";
cin >> size;
blocks.push_back({i + 1, size, false});
}
}
void inputProcesses(vector<Process> &processes, int m) {
for (int i = 0; i < m; i++) {
int size;
cout << "Enter size of process " << i + 1 << ": ";
cin >> size;
processes.push_back({i + 1, size, -1});
}
}
1️⃣ First Fit Algorithm
👇 Code
void firstFit(vector<Block> blocks, vector<Process> processes) {
cout << "\n--- First Fit Allocation ---\n";
for (auto &p : processes) {
for (auto &b : blocks) {
if (!b.allocated && b.size >= p.size) {
p.blockAllocated = b.id;
b.allocated = true;
break;
}
}
}
for (auto &p : processes) {
if (p.blockAllocated != -1)
cout << "Process " << p.id << " allocated to Block " << p.blockAllocated << endl;
else
cout << "Process " << p.id << " not allocated\n";
}
}
✅ Explanation
-
We go through each process and check every block.
-
If the block is not allocated and has enough space, we allocate it immediately.
-
This is the "First Fit" strategy — assign the first available suitable block.
2️⃣ Best Fit Algorithm
👇 Code
void bestFit(vector<Block> blocks, vector<Process> processes) {
cout << "\n--- Best Fit Allocation ---\n";
for (auto &p : processes) {
int bestIdx = -1;
for (int i = 0; i < blocks.size(); i++) {
if (!blocks[i].allocated && blocks[i].size >= p.size) {
if (bestIdx == -1 || blocks[i].size < blocks[bestIdx].size)
bestIdx = i;
}
}
if (bestIdx != -1) {
blocks[bestIdx].allocated = true;
p.blockAllocated = blocks[bestIdx].id;
}
}
for (auto &p : processes) {
if (p.blockAllocated != -1)
cout << "Process " << p.id << " allocated to Block " << p.blockAllocated << endl;
else
cout << "Process " << p.id << " not allocated\n";
}
}
✅ Explanation
-
We search for the smallest available block that is large enough for the process.
-
Among all suitable blocks, we pick the best (smallest) fit.
3️⃣ Worst Fit Algorithm
👇 Code
void worstFit(vector<Block> blocks, vector<Process> processes) {
cout << "\n--- Worst Fit Allocation ---\n";
for (auto &p : processes) {
int worstIdx = -1;
for (int i = 0; i < blocks.size(); i++) {
if (!blocks[i].allocated && blocks[i].size >= p.size) {
if (worstIdx == -1 || blocks[i].size > blocks[worstIdx].size)
worstIdx = i;
}
}
if (worstIdx != -1) {
blocks[worstIdx].allocated = true;
p.blockAllocated = blocks[worstIdx].id;
}
}
for (auto &p : processes) {
if (p.blockAllocated != -1)
cout << "Process " << p.id << " allocated to Block " << p.blockAllocated << endl;
else
cout << "Process " << p.id << " not allocated\n";
}
}
✅ Explanation
-
We look for the largest available block.
-
This strategy tries to avoid leaving behind small fragments of memory.
🧪 Final: Main Function to Run the Simulator
int main() {
vector<Block> blocks;
vector<Process> processes;
int n, m;
cout << "Enter number of memory blocks: ";
cin >> n;
inputBlocks(blocks, n);
cout << "Enter number of processes: ";
cin >> m;
inputProcesses(processes, m);
firstFit(blocks, processes);
bestFit(blocks, processes);
worstFit(blocks, processes);
return 0;
}
🧠 Summary Table of Algorithms
Strategy | Best For | Drawback |
---|---|---|
First Fit | Fast and simple | May cause fragmentation |
Best Fit | Reduces memory waste | Can be slow due to searching |
Worst Fit | Leaves large blocks behind | May increase fragmentation |
🎯 Conclusion
In this article, we created a simple memory allocation simulator using C++. We used:
-
First Fit: Fastest, less efficient.
-
Best Fit: More efficient but slower.
-
Worst Fit: May seem counter-intuitive but useful in specific cases.
Each strategy has its own strengths and weaknesses. Understanding these helps in designing better operating systems or applications that handle memory efficiently.