Loading content...
Given a fixed pool of frames and a set of processes competing for them, how should the operating system divide this precious resource? This question lies at the heart of frame allocation, and there is no single perfect answer.
Different allocation strategies embody different values and assumptions:
Each strategy has strengths and weaknesses. The right choice depends on the workload characteristics, system goals, and the tradeoffs the system administrator is willing to make. Understanding these strategies deeply enables informed selection and tuning.
By the end of this page, you will understand the major frame allocation strategies, their mathematical foundations, implementation techniques, and when each is appropriate. You'll be able to analyze the tradeoffs and select or combine strategies for different scenarios.
The simplest allocation strategy: divide available frames equally among all processes.
Formula:
frames_per_process = available_frames / num_processes
If we have 1000 available frames and 10 processes, each process gets 100 frames. Simple, deterministic, and easy to implement.
Advantages:
Disadvantages:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
/* * Equal Frame Allocation * * The simplest allocation strategy: divide frames equally. */ #include <stdio.h>#include <stdlib.h> #define MAX_PROCESSES 100 typedef struct { int pid; int size_pages; // Total address space size int working_set_size; // Current WSS int allocated_frames; // Frames allocated int page_faults; // Fault count for efficiency calc} Process; typedef struct { int total_frames; int reserved_frames; // For OS, not allocatable int num_processes; Process processes[MAX_PROCESSES];} System; /* * Simple equal allocation */void equal_allocation(System *sys) { int available = sys->total_frames - sys->reserved_frames; int per_process = available / sys->num_processes; printf("Equal Allocation Strategy"); printf("========================="); printf("Available frames: %d", available); printf("Processes: %d", sys->num_processes); printf("Frames per process: %d ", per_process); for (int i = 0; i < sys->num_processes; i++) { sys->processes[i].allocated_frames = per_process; }} /* * Analyze efficiency of equal allocation */void analyze_equal_allocation(System *sys) { printf("%-5s %-12s %-12s %-12s %-10s", "PID", "WSS", "Allocated", "Difference", "Status"); printf("-----------------------------------------------------"); int total_wasted = 0; int total_deficit = 0; int thrashing = 0; for (int i = 0; i < sys->num_processes; i++) { Process *p = &sys->processes[i]; int diff = p->allocated_frames - p->working_set_size; const char *status; if (diff >= 0) { status = "OK"; total_wasted += diff; } else { if (-diff > p->working_set_size / 2) { status = "THRASHING"; thrashing++; } else { status = "Struggling"; } total_deficit -= diff; } printf("%-5d %-12d %-12d %-12d %-10s", p->pid, p->working_set_size, p->allocated_frames, diff, status); } printf("-----------------------------------------------------"); printf("Total frames wasted: %d", total_wasted); printf("Total frame deficit: %d", total_deficit); printf("Processes thrashing: %d ", thrashing); if (total_wasted > 0 && total_deficit > 0) { printf("INEFFICIENCY: %d frames wasted while %d processes starve!", total_wasted, thrashing); printf("Equal allocation fails when process sizes differ."); }} /* * Example scenario showing equal allocation problems: * * System: 1000 frames, 5 processes * Each process gets: 200 frames * * Process A: WSS = 50 -> 150 frames wasted * Process B: WSS = 80 -> 120 frames wasted * Process C: WSS = 150 -> 50 frames wasted * Process D: WSS = 400 -> 200 frame deficit, THRASHING * Process E: WSS = 320 -> 120 frame deficit, struggling * * Total wasted: 320 frames * Total deficit: 320 frames * * The inefficiency is symmetric: we could reallocate wasted * frames to struggling processes, but equal allocation doesn't. */Equal allocation works well only when processes have similar memory requirements. When process sizes vary significantly (common in real systems), equal allocation results in both waste and starvation simultaneously—a clear sign of suboptimal policy.
Proportional allocation divides frames based on process size, ensuring larger processes receive more frames.
Formula:
For process i with size Si:
allocation_i = (Si / ΣSj) × available_frames
Where ΣSj is the sum of all process sizes
Example:
Available frames: 1000
Each process gets frames in proportion to its size relative to total demand.
What "size" means:
Proportional allocation requires a measure of process size. Options include:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
/* * Proportional Frame Allocation * * Allocates frames based on process size relative to total. */ #include <stdio.h>#include <stdlib.h> #define MAX_PROCESSES 100#define MIN_FRAMES 4 // Architectural minimum typedef struct { int pid; int size; // Size metric (VM size, WSS, etc.) int allocated; int min_guaranteed; // Never go below this} ProcessInfo; /* * Proportional allocation with minimum guarantee */void proportional_allocation(ProcessInfo procs[], int n, int available_frames) { printf("Proportional Allocation"); printf("======================= "); // Calculate total size int total_size = 0; for (int i = 0; i < n; i++) { total_size += procs[i].size; } printf("Total process size: %d pages", total_size); printf("Available frames: %d ", available_frames); // First pass: allocate proportionally int allocated_sum = 0; for (int i = 0; i < n; i++) { // Proportional share int share = (procs[i].size * available_frames) / total_size; // Ensure minimum if (share < procs[i].min_guaranteed) { share = procs[i].min_guaranteed; } procs[i].allocated = share; allocated_sum += share; } // Handle rounding: distribute remaining frames int remaining = available_frames - allocated_sum; while (remaining > 0) { // Give extra frames to largest processes first for (int i = 0; i < n && remaining > 0; i++) { procs[i].allocated++; remaining--; } } // Handle overallocation: take from largest processes while (remaining < 0) { for (int i = n-1; i >= 0 && remaining < 0; i--) { if (procs[i].allocated > procs[i].min_guaranteed) { procs[i].allocated--; remaining++; } } } // Print results printf("%-5s %-10s %-12s %-10s", "PID", "Size", "Proportion", "Allocated"); printf("----------------------------------------"); for (int i = 0; i < n; i++) { double proportion = (double)procs[i].size / total_size * 100; printf("%-5d %-10d %9.1f%% %-10d", procs[i].pid, procs[i].size, proportion, procs[i].allocated); } printf("----------------------------------------"); printf("Total allocated: %d", available_frames);} /* * Compare proportional vs equal allocation */void compare_strategies() { ProcessInfo procs[] = { {1, 100, 0, MIN_FRAMES}, {2, 200, 0, MIN_FRAMES}, {3, 300, 0, MIN_FRAMES}, {4, 50, 0, MIN_FRAMES}, {5, 350, 0, MIN_FRAMES}, }; int n = 5; int available = 500; printf("=== Comparison: Equal vs Proportional === "); // Calculate total size int total = 0; for (int i = 0; i < n; i++) { total += procs[i].size; } printf("%-5s %-8s %-10s %-12s", "PID", "Size", "Equal", "Proportional"); printf("--------------------------------------"); int equal_per_proc = available / n; for (int i = 0; i < n; i++) { int prop = (procs[i].size * available) / total; printf("%-5d %-8d %-10d %-12d", procs[i].pid, procs[i].size, equal_per_proc, prop); } printf("With proportional allocation:"); printf(" - Small processes (50 pages) get 25 frames"); printf(" - Large processes (350 pages) get 175 frames"); printf(" - Better match between need and allocation");} /* * Advantages of proportional allocation: * 1. Larger processes get more frames (matches intuition) * 2. Reduces waste from over-allocating small processes * 3. Reduces thrashing in large processes * * Disadvantages: * 1. Assumes size ~ need (not always true) * 2. What size metric to use? * 3. Still doesn't consider urgency or priority * 4. A process with huge VM but tiny WSS gets too much */Using working set size (WSS) instead of virtual memory size gives better results. A process with a large address space but small active region doesn't need as many frames as its VM size suggests. WSS-proportional allocation matches actual needs more closely.
Priority-based allocation favors important processes, giving them more frames than their size alone would warrant.
Motivation:
Not all processes are equally important:
Approaches:
Priority multiplier: High-priority processes get a larger share
effective_size = actual_size × priority_multiplier
Priority tiers: Guarantee minimum allocations per tier
Priority stealing: High-priority processes can take frames from low-priority ones during memory pressure
Reserve pools: High-priority processes draw from a reserved frame pool not available to lower priorities
| Process | Size | Priority | Multiplier | Effective Size | Allocation |
|---|---|---|---|---|---|
| Audio Player | 100 | Real-time | ×3.0 | 300 | 188 frames |
| Web Browser | 400 | Interactive | ×1.5 | 600 | 375 frames |
| Background Sync | 200 | Background | ×0.5 | 100 | 63 frames |
| Compiler | 500 | Normal | ×1.0 | 500 | 313 frames |
| Backup Job | 300 | Low | ×0.3 | 90 | 56 frames |
Implementation considerations:
Priority inversion: A high-priority process waiting for a low-priority one (e.g., for a lock) may suffer if the low-priority process is starved of frames
Starvation avoidance: Low-priority processes must still get minimum frames to avoid livelock
Dynamic priority: Priority may change during execution based on behavior or aging
User vs system priority: Kernel threads may have higher base priority than user processes
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
/* * Priority-Based Frame Allocation * * Allocates frames considering both size and priority. */ #include <stdio.h>#include <stdlib.h> typedef enum { PRIORITY_REALTIME = 4, PRIORITY_INTERACTIVE = 3, PRIORITY_NORMAL = 2, PRIORITY_BACKGROUND = 1, PRIORITY_IDLE = 0} Priority; typedef struct { int pid; const char *name; int size; // Working set size Priority priority; double multiplier; // Priority-based multiplier int allocated; int minimum; // Guaranteed minimum} PriorityProcess; double get_multiplier(Priority p) { switch (p) { case PRIORITY_REALTIME: return 3.0; case PRIORITY_INTERACTIVE: return 1.5; case PRIORITY_NORMAL: return 1.0; case PRIORITY_BACKGROUND: return 0.5; case PRIORITY_IDLE: return 0.25; default: return 1.0; }} const char* priority_name(Priority p) { switch (p) { case PRIORITY_REALTIME: return "Real-time"; case PRIORITY_INTERACTIVE: return "Interactive"; case PRIORITY_NORMAL: return "Normal"; case PRIORITY_BACKGROUND: return "Background"; case PRIORITY_IDLE: return "Idle"; default: return "Unknown"; }} void priority_allocation(PriorityProcess procs[], int n, int available_frames) { printf("Priority-Based Allocation"); printf("========================= "); // Calculate effective sizes double total_effective = 0; for (int i = 0; i < n; i++) { procs[i].multiplier = get_multiplier(procs[i].priority); total_effective += procs[i].size * procs[i].multiplier; } // First pass: ensure all minimums int minimum_total = 0; for (int i = 0; i < n; i++) { minimum_total += procs[i].minimum; } if (minimum_total > available_frames) { printf("ERROR: Cannot satisfy minimum requirements!"); return; } int distributable = available_frames - minimum_total; // Allocate based on priority-weighted size for (int i = 0; i < n; i++) { double effective_size = procs[i].size * procs[i].multiplier; int extra = (int)((effective_size / total_effective) * distributable); procs[i].allocated = procs[i].minimum + extra; } // Display results printf("Available: %d frames, Minimum reserved: %d, " "Distributable: %d ", available_frames, minimum_total, distributable); printf("%-15s %-6s %-12s %-6s %-10s %-10s", "Process", "Size", "Priority", "Mult", "Effective", "Allocated"); printf("----------------------------------------------------------------"); for (int i = 0; i < n; i++) { printf("%-15s %-6d %-12s ×%-5.1f %-10.0f %-10d", procs[i].name, procs[i].size, priority_name(procs[i].priority), procs[i].multiplier, procs[i].size * procs[i].multiplier, procs[i].allocated); }} /* * Priority-based reclamation under memory pressure */void reclaim_from_low_priority(PriorityProcess procs[], int n, int frames_needed) { printf("Memory Pressure: Need to reclaim %d frames", frames_needed); printf("Reclaiming from lowest priority processes first: "); int reclaimed = 0; // Start with lowest priority for (Priority p = PRIORITY_IDLE; p <= PRIORITY_REALTIME && reclaimed < frames_needed; p++) { for (int i = 0; i < n && reclaimed < frames_needed; i++) { if (procs[i].priority == p && procs[i].allocated > procs[i].minimum) { int can_take = procs[i].allocated - procs[i].minimum; int taking = (can_take < frames_needed - reclaimed) ? can_take : frames_needed - reclaimed; printf(" Taking %d frames from %s (was %d, now %d)", taking, procs[i].name, procs[i].allocated, procs[i].allocated - taking); procs[i].allocated -= taking; reclaimed += taking; } } } if (reclaimed < frames_needed) { printf("WARNING: Could only reclaim %d of %d needed frames!", reclaimed, frames_needed); printf("Consider swapping or terminating processes."); }}Priority allocation can lead to starvation if not managed carefully. A steady stream of high-priority processes could prevent low-priority ones from ever making progress. Most systems combine priority with aging—processes that wait too long have their effective priority increased.
Real-world systems rarely use pure allocation strategies. Instead, they combine elements of multiple approaches and adapt dynamically to changing conditions.
Hybrid strategy elements:
Minimum guarantee (from absolute minimum): Every process gets at least the architectural minimum
Base allocation (from equal or proportional): After minimums, remaining frames are divided
Priority adjustment (from priority-based): High-priority processes get more than their share
Working set tracking (dynamic): Allocations adjust based on observed behavior
Memory pressure response (dynamic): Under pressure, reclaim from appropriate processes
Dynamic allocation benefits:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
/* * Dynamic Frame Allocation * * Adapts allocation based on observed process behavior * and system-wide memory pressure. */ #include <stdio.h>#include <stdlib.h>#include <stdbool.h> #define HIGH_PRESSURE_THRESHOLD 0.9 // 90% memory used#define LOW_PRESSURE_THRESHOLD 0.7 // 70% memory used#define PFF_HIGH 10.0 // Faults/sec - need more frames#define PFF_LOW 1.0 // Faults/sec - can reduce frames typedef enum { PRESSURE_LOW, PRESSURE_MEDIUM, PRESSURE_HIGH, PRESSURE_CRITICAL} MemoryPressure; typedef struct { int pid; int allocated_frames; int minimum_frames; int maximum_frames; // Upper limit // Observed behavior double page_fault_rate; // Faults per second int working_set_estimate; time_t last_adjustment; // Adjustment tracking int frames_to_give; // Positive = should get more bool can_take_frames; // Can we take from this process?} DynamicProcess; typedef struct { int total_frames; int used_frames; MemoryPressure pressure; int adjustment_interval_ms;} SystemState; /* * Determine memory pressure level */MemoryPressure calculate_pressure(SystemState *state) { double utilization = (double)state->used_frames / state->total_frames; if (utilization > 0.95) return PRESSURE_CRITICAL; if (utilization > 0.90) return PRESSURE_HIGH; if (utilization > 0.70) return PRESSURE_MEDIUM; return PRESSURE_LOW;} /* * Page Fault Frequency based adjustment * * If fault rate is high, process needs more frames. * If fault rate is low, frames could be taken. */void pff_adjustment(DynamicProcess procs[], int n) { printf("PFF-Based Allocation Adjustment"); printf("================================ "); for (int i = 0; i < n; i++) { DynamicProcess *p = &procs[i]; printf("PID %d: Fault rate = %.2f/sec", p->pid, p->page_fault_rate); if (p->page_fault_rate > PFF_HIGH) { // Need more frames int need = (p->working_set_estimate - p->allocated_frames); need = (need > 0) ? need : p->allocated_frames / 10; p->frames_to_give = need; p->can_take_frames = false; printf(" -> NEEDS %d more frames (high fault rate)", need); } else if (p->page_fault_rate < PFF_LOW) { // Can potentially give up frames int excess = p->allocated_frames - p->working_set_estimate; excess = (excess > 0) ? excess : 0; p->frames_to_give = 0; p->can_take_frames = (excess > 0); printf(" -> CAN GIVE UP ~%d frames (low fault rate)", excess); } else { // In acceptable range p->frames_to_give = 0; p->can_take_frames = false; printf(" -> STABLE (acceptable fault rate)"); } }} /* * Rebalance frames between processes */void rebalance_frames(DynamicProcess procs[], int n, SystemState *state) { printf("Rebalancing Frames"); printf("================== "); // Find processes that need frames int total_need = 0; for (int i = 0; i < n; i++) { if (procs[i].frames_to_give > 0) { total_need += procs[i].frames_to_give; } } if (total_need == 0) { printf("No processes need additional frames."); return; } printf("Total frames needed: %d", total_need); // Find frames that can be taken int available = 0; for (int i = 0; i < n; i++) { if (procs[i].can_take_frames) { int excess = procs[i].allocated_frames - procs[i].working_set_estimate; if (excess > 0) { available += excess; } } } // Also check free frames int free_frames = state->total_frames - state->used_frames; available += free_frames; printf("Available to redistribute: %d ", available); // Redistribute (simplified) int to_distribute = (available < total_need) ? available : total_need; for (int i = 0; i < n && to_distribute > 0; i++) { if (procs[i].frames_to_give > 0) { int give = (procs[i].frames_to_give < to_distribute) ? procs[i].frames_to_give : to_distribute; // Respect maximum if (procs[i].allocated_frames + give > procs[i].maximum_frames) { give = procs[i].maximum_frames - procs[i].allocated_frames; } printf("PID %d: Adding %d frames (%d -> %d)", procs[i].pid, give, procs[i].allocated_frames, procs[i].allocated_frames + give); procs[i].allocated_frames += give; to_distribute -= give; } } if (to_distribute < total_need) { printf("Note: Only partially satisfied needs."); printf("Consider swapping out low-priority processes."); }}Linux uses a dynamic, demand-driven approach. It doesn't pre-allocate fixed frames per process. Instead, it uses page replacement (LRU variants) and reclamation from file caches. Under memory pressure, it uses kswapd to proactively reclaim pages and the OOM killer as a last resort.
Each allocation strategy has its place. Let's compare them systematically.
When to use each strategy:
Equal Allocation:
Proportional Allocation:
Priority-Based Allocation:
Dynamic Allocation:
| Criterion | Equal | Proportional | Priority | Dynamic |
|---|---|---|---|---|
| Implementation Complexity | ★☆☆☆☆ | ★★☆☆☆ | ★★★☆☆ | ★★★★★ |
| Efficiency (matched to need) | ★★☆☆☆ | ★★★★☆ | ★★★★☆ | ★★★★★ |
| Fairness (equal treatment) | ★★★★★ | ★★★★☆ | ★★☆☆☆ | ★★★☆☆ |
| Predictability | ★★★★★ | ★★★★☆ | ★★★☆☆ | ★★☆☆☆ |
| Responsiveness to change | ☆☆☆☆☆ | ★★☆☆☆ | ★★☆☆☆ | ★★★★★ |
| Prevents thrashing | ★☆☆☆☆ | ★★★☆☆ | ★★★☆☆ | ★★★★★ |
| Overhead | ☆☆☆☆☆ | ★☆☆☆☆ | ★★☆☆☆ | ★★★★☆ |
Implementing frame allocation strategies requires careful attention to several practical concerns.
Counting frames:
Track per-process frame counts accurately:
Enforcement mechanisms:
How does the OS prevent a process from exceeding its allocation?
Reclamation targets:
When a process needs frames, where do they come from?
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
/* * Practical Allocation Implementation * * Demonstrates key implementation aspects of frame allocation. */ #include <stdio.h>#include <stdlib.h>#include <stdbool.h> typedef struct { int id; int rss; // Resident Set Size int limit; // Maximum allowed frames int min_required; // Minimum required frames int shared_pages; // Pages shared with other processes} ProcessAllocation; typedef struct { int total_frames; int free_frames; int cache_frames; // File cache, can be reclaimed int process_count; ProcessAllocation *processes;} FrameManager; /* * Check if allocation is within limits */bool can_allocate(FrameManager *fm, ProcessAllocation *proc) { // Check process limit if (proc->rss >= proc->limit) { printf("PID %d: At allocation limit (%d frames)", proc->id, proc->limit); return false; } // Check system-wide availability if (fm->free_frames == 0) { printf("PID %d: No free frames available", proc->id); return false; } return true;} /* * Try to allocate a frame to a process */bool allocate_frame(FrameManager *fm, ProcessAllocation *proc) { if (!can_allocate(fm, proc)) { // Try reclamation if (fm->cache_frames > 0) { // Reclaim from page cache fm->cache_frames--; fm->free_frames++; printf(" Reclaimed 1 frame from page cache"); } else { // Need to swap or deny printf(" No reclaimable frames!"); return false; } } // Allocate from free list fm->free_frames--; proc->rss++; return true;} /* * Reclaim frames from a process */int reclaim_frames(FrameManager *fm, ProcessAllocation *proc, int count) { int reclaimed = 0; int can_reclaim = proc->rss - proc->min_required; if (can_reclaim <= 0) { printf("PID %d: Cannot reclaim - at minimum", proc->id); return 0; } int to_reclaim = (count < can_reclaim) ? count : can_reclaim; // Don't count shared pages as reclaimable (simplification) if (proc->shared_pages > 0) { int private = proc->rss - proc->shared_pages; if (private - proc->min_required < to_reclaim) { to_reclaim = private - proc->min_required; if (to_reclaim < 0) to_reclaim = 0; } } proc->rss -= to_reclaim; fm->free_frames += to_reclaim; reclaimed = to_reclaim; printf("PID %d: Reclaimed %d frames (RSE now %d)", proc->id, reclaimed, proc->rss); return reclaimed;} /* * Enforce allocation limits */void enforce_limits(FrameManager *fm) { printf("Enforcing Allocation Limits"); printf("=========================== "); for (int i = 0; i < fm->process_count; i++) { ProcessAllocation *p = &fm->processes[i]; if (p->rss > p->limit) { int excess = p->rss - p->limit; printf("PID %d: %d frames over limit", p->id, excess); int reclaimed = reclaim_frames(fm, p, excess); if (reclaimed < excess) { printf(" WARNING: Could not fully enforce limit!"); } } }} /* * Handle memory pressure */void handle_memory_pressure(FrameManager *fm, int target_free) { printf("Memory Pressure Handler"); printf("Target free frames: %d, Current: %d ", target_free, fm->free_frames); while (fm->free_frames < target_free) { // First: page cache if (fm->cache_frames > 0) { int take = (fm->cache_frames < 10) ? fm->cache_frames : 10; fm->cache_frames -= take; fm->free_frames += take; printf("Reclaimed %d frames from page cache", take); continue; } // Second: find process with most excess ProcessAllocation *victim = NULL; int max_excess = 0; for (int i = 0; i < fm->process_count; i++) { ProcessAllocation *p = &fm->processes[i]; int excess = p->rss - p->min_required; if (excess > max_excess) { max_excess = excess; victim = p; } } if (victim && max_excess > 0) { int take = (max_excess < 10) ? max_excess : 10; reclaim_frames(fm, victim, take); } else { printf("CRITICAL: Cannot reclaim more frames!"); printf("Consider OOM killer or swapping."); break; } }}Shared pages (shared libraries, copy-on-write, shared memory) complicate accounting. Do we charge each sharing process fully, proportionally, or just once? Different systems make different choices, but the decision affects how allocation limits are interpreted.
Let's examine how real operating systems implement frame allocation.
Linux:
cgroups provide container/process group limitsvm.overcommit_memory controls overcommit policy/proc/sys/vm/ provides tuning knobsWindows:
QUOTA_LIMITS structure defines per-process limitsmacOS:
| Feature | Linux | Windows | macOS |
|---|---|---|---|
| Default Limits | None (cgroups optional) | Working set min/max | Dynamic |
| Allocation Strategy | Demand + replace | Working set trimming | Compression-first |
| Priority Support | cgroup weights | Process priority | App priority tiers |
| Pressure Response | kswapd, OOM killer | Balance Set Manager | Memory pressure, Jetsam |
| Tuning Mechanism | sysctl, cgroups | API (SetProcessWorkingSetSize) | Automatic |
Modern OSes have moved away from static allocation toward dynamic, adaptive approaches. They monitor behavior, respond to pressure, and adjust allocation in real-time. Static policies like pure equal or proportional allocation are mainly used in specialized environments (embedded, real-time).
We've explored the major strategies for dividing frames among competing processes. Each approach embodies different assumptions and values, and the right choice depends on workload characteristics and system goals.
What's next:
Allocation strategies determine the initial distribution of frames, but what about ongoing fairness and adaptation? The final page in this module explores fairness considerations—how to ensure equitable treatment over time and prevent any process from being permanently disadvantaged.
You now understand the major frame allocation strategies—from simple equal division to sophisticated dynamic adaptation. This knowledge enables you to analyze, design, and tune memory management policies for diverse workloads.