Memory fragmentation occurs when memory is divided into small, non-contiguous blocks, making it difficult to allocate larger blocks of memory even if the total free memory is sufficient. Fragmentation can significantly degrade system performance and lead to inefficient memory utilization.
Memory fragmentation can be categorized into internal and external fragmentation. Below is an implementation of a memory manager that analyzes fragmentation:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct MemoryBlock {
size_t size;
bool is_allocated;
void *start_address;
struct MemoryBlock *next;
struct MemoryBlock *prev;
} MemoryBlock;
typedef struct {
MemoryBlock *head;
size_t total_size;
size_t used_size;
size_t internal_fragmentation;
size_t external_fragmentation;
} MemoryManager;
void analyze_fragmentation(MemoryManager *manager) {
MemoryBlock *current = manager->head;
size_t total_internal = 0;
size_t total_external = 0;
size_t largest_free_block = 0;
while (current != NULL) {
if (current->is_allocated) {
// Calculate internal fragmentation
size_t block_internal = current->size -
get_actual_data_size(current);
total_internal += block_internal;
} else {
// Track external fragmentation
total_external += current->size;
if (current->size > largest_free_block) {
largest_free_block = current->size;
}
}
current = current->next;
}
manager->internal_fragmentation = total_internal;
manager->external_fragmentation = total_external - largest_free_block;
}
Defragmentation involves reorganizing memory to reduce fragmentation. Below is an implementation of a defragmentation algorithm:
typedef struct {
void *old_address;
void *new_address;
size_t size;
} RelocationEntry;
typedef struct {
RelocationEntry *entries;
size_t count;
size_t capacity;
} RelocationTable;
bool defragment_memory(MemoryManager *manager) {
RelocationTable relocation_table = {
.entries = malloc(1000 * sizeof(RelocationEntry)),
.count = 0,
.capacity = 1000
};
// Phase 1: Analyze and plan relocations
MemoryBlock *current = manager->head;
void *next_free_address = manager->head->start_address;
while (current != NULL) {
if (current->is_allocated) {
if (current->start_address != next_free_address) {
// Need to relocate this block
RelocationEntry entry = {
.old_address = current->start_address,
.new_address = next_free_address,
.size = current->size
};
relocation_table.entries[relocation_table.count++] = entry;
}
next_free_address += current->size;
}
current = current->next;
}
// Phase 2: Perform relocations
for (size_t i = 0; i < relocation_table.count; i++) {
RelocationEntry *entry = &relocation_table.entries[i];
memmove(entry->new_address, entry->old_address, entry->size);
update_pointers(manager, entry);
}
// Phase 3: Update memory block list
coalesce_free_blocks(manager);
free(relocation_table.entries);
return true;
}
Memory compaction involves moving allocated memory blocks to reduce fragmentation. Below is an implementation of memory compaction:
typedef struct {
void **pointers;
size_t count;
} PointerRegistry;
bool compact_memory(MemoryManager *manager, PointerRegistry *registry) {
// Phase 1: Mark all blocks
mark_live_blocks(manager);
// Phase 2: Calculate new addresses
void *compact_address = manager->head->start_address;
MemoryBlock *current = manager->head;
while (current != NULL) {
if (current->is_allocated) {
current->new_address = compact_address;
compact_address += current->size;
}
current = current->next;
}
// Phase 3: Update pointers
for (size_t i = 0; i < registry->count; i++) {
void *old_ptr = *registry->pointers[i];
MemoryBlock *block = find_block_for_address(manager, old_ptr);
if (block != NULL) {
size_t offset = (char*)old_ptr - (char*)block->start_address;
*registry->pointers[i] = (char*)block->new_address + offset;
}
}
// Phase 4: Move memory
current = manager->head;
while (current != NULL) {
if (current->is_allocated &&
current->start_address != current->new_address) {
memmove(current->new_address,
current->start_address,
current->size);
current->start_address = current->new_address;
}
current = current->next;
}
return true;
}
Memory pools allocate fixed-size blocks of memory, reducing fragmentation. Below is an implementation of a memory pool:
typedef struct {
void *pool_start;
size_t block_size;
size_t num_blocks;
uint8_t *block_status; // 0 = free, 1 = allocated
void **free_blocks;
size_t free_count;
} MemoryPool;
MemoryPool* create_memory_pool(size_t block_size, size_t num_blocks) {
MemoryPool *pool = malloc(sizeof(MemoryPool));
pool->block_size = block_size;
pool->num_blocks = num_blocks;
pool->pool_start = aligned_alloc(sizeof(void*),
block_size * num_blocks);
pool->block_status = calloc(num_blocks, sizeof(uint8_t));
pool->free_blocks = malloc(num_blocks * sizeof(void*));
pool->free_count = num_blocks;
// Initialize free block list
for (size_t i = 0; i < num_blocks; i++) {
pool->free_blocks[i] = (char*)pool->pool_start +
(i * block_size);
}
return pool;
}
void* pool_allocate(MemoryPool *pool) {
if (pool->free_count == 0) return NULL;
void *block = pool->free_blocks[--pool->free_count];
size_t block_index = ((char*)block - (char*)pool->pool_start) /
pool->block_size;
pool->block_status[block_index] = 1;
return block;
}
The buddy system is an advanced memory allocation strategy that reduces fragmentation by splitting and merging memory blocks.
Below is an implementation of the buddy system:
typedef struct {
void *memory;
size_t total_size;
size_t min_block_size;
size_t max_order;
struct list_head *free_lists;
} BuddyAllocator;
BuddyAllocator* create_buddy_allocator(size_t total_size,
size_t min_block_size) {
BuddyAllocator *allocator = malloc(sizeof(BuddyAllocator));
allocator->total_size = total_size;
allocator->min_block_size = min_block_size;
allocator->max_order = log2(total_size / min_block_size);
allocator->memory = aligned_alloc(min_block_size, total_size);
// Initialize free lists
allocator->free_lists = malloc(sizeof(struct list_head) *
(allocator->max_order + 1));
for (size_t i = 0; i <= allocator->max_order; i++) {
INIT_LIST_HEAD(&allocator->free_lists[i]);
}
// Add initial block to highest order free list
add_to_free_list(allocator, allocator->memory, allocator->max_order);
return allocator;
}
void* buddy_allocate(BuddyAllocator *allocator, size_t size) {
size_t order = calculate_order(size, allocator->min_block_size);
// Find suitable block
for (size_t i = order; i <= allocator->max_order; i++) {
if (!list_empty(&allocator->free_lists[i])) {
void *block = remove_from_free_list(allocator, i);
// Split block if necessary
while (i > order) {
i--;
void *buddy = (char*)block + (1 << i);
add_to_free_list(allocator, buddy, i);
}
return block;
}
}
return NULL;
}
Fragmentation prevention involves strategies to minimize fragmentation during memory allocation.
Monitoring tools help analyze memory usage and fragmentation. Below is an implementation of memory monitoring:
typedef struct {
size_t total_allocations;
size_t total_deallocations;
size_t peak_memory_usage;
size_t current_memory_usage;
double fragmentation_ratio;
size_t largest_contiguous_free;
} MemoryStats;
void collect_memory_stats(MemoryManager *manager, MemoryStats *stats) {
MemoryBlock *current = manager->head;
size_t total_free = 0;
size_t largest_free = 0;
size_t current_free = 0;
while (current != NULL) {
if (current->is_allocated) {
current_free = 0;
} else {
current_free += current->size;
total_free += current->size;
if (current_free > largest_free) {
largest_free = current_free;
}
}
current = current->next;
}
stats->largest_contiguous_free = largest_free;
stats->fragmentation_ratio = 1.0 -
((double)largest_free / total_free);
}
Key performance considerations for memory fragmentation management include:
Memory fragmentation management is crucial for system performance and reliability. Understanding and implementing proper fragmentation prevention and management techniques can significantly improve system efficiency and resource utilization.