A custom implementation of malloc, free, and realloc in C, featuring segregated free lists, quick lists for fast allocation, and automatic memory coalescing to minimize fragmentation.
This project implements a dynamic memory allocator that manages heap memory efficiently using advanced techniques found in production allocators. The allocator provides drop-in replacements for the standard C library functions malloc(), free(), and realloc().
sf_malloc(size_t size)- Allocates memory blocks with requested sizesf_free(void *ptr)- Frees allocated memory and returns it to the heapsf_realloc(void *ptr, size_t size)- Resizes previously allocated memory blocks
- Uses multiple free lists organized by block size classes
- Block sizes: 32, 64, 128, 256, 512, 1024, 2048, 4096+ bytes
- Each list manages blocks within a specific size range
- Enables O(1) insertion and fast size-appropriate block lookup
- Maintains separate quick lists for frequently allocated small blocks (32-176 bytes)
- LIFO (Last-In-First-Out) allocation for cache efficiency
- Defers coalescing for recently freed blocks to speed up malloc/free cycles
- Automatically flushes to main free lists when capacity is reached
- Immediate coalescing for large blocks to reduce external fragmentation
- Delayed coalescing for small blocks via quick lists
- Bidirectional coalescing merges adjacent free blocks
- Prevents fragmentation by combining neighboring free memory
- Splits large free blocks when a smaller allocation is requested
- Prevents internal fragmentation by avoiding wasted space
- Maintains minimum block size of 32 bytes to avoid splinters
Each memory block contains:
- Header (8 bytes): Stores block size, allocation status, and payload size
- Payload: User-accessible memory
- Footer (8 bytes): Duplicate of header for backward traversal
[Padding: 8B] [Prologue: 32B] [Free/Allocated Blocks] [Epilogue: 8B]
- Blocks are 16-byte aligned
- Minimum block size: 32 bytes (header + footer + 16 bytes)
- Header format:
[32-bit payload size][32-bit size + flags] - Flags:
THIS_BLOCK_ALLOCATED,IN_QUICK_LIST
- All metadata is XOR-encrypted with a magic number to detect corruption
- Provides basic protection against accidental overwrites
Calculates internal fragmentation ratio:
fragmentation = total_allocated_payloads / total_allocated_block_size
Measures peak memory utilization:
utilization = peak_payload_size / total_heap_size
| Operation | Average Case | Worst Case |
|---|---|---|
sf_malloc() |
O(1) - O(log n) | O(n) |
sf_free() |
O(1) | O(1) |
sf_realloc() |
O(1) - O(n) | O(n) |
| Coalescing | O(1) | O(1) |
- Quick list hits: O(1)
- Free list search: O(log n) for segregated lists, O(n) worst case
- Coalescing: O(1) - only checks immediate neighbors
- Check quick lists - If block size ≤ 176 bytes, attempt quick list allocation
- Search free lists - Find first-fit block in appropriate size class
- Expand heap - If no suitable block exists, request more memory via
sf_mem_grow() - Split blocks - Divide larger blocks to minimize waste
- Update metadata - Track payload sizes for fragmentation metrics
- Returns
NULLand setssf_errno = ENOMEMwhen out of memory - Calls
abort()for invalid operations:- Freeing
NULLpointer - Double-free detection
- Freeing already quick-listed blocks
- Invalid pointer alignment or size
- Freeing
- Minimum block size: 32 bytes
- Alignment: 16 bytes
- Quick list capacity: 5 blocks per size class
- Quick list sizes: 32, 48, 64, 80, 96, 112, 128, 144, 160, 176 bytes
- Page size: 4096 bytes (system-dependent)
This allocator is designed to be compiled as part of a larger system programming project. It requires:
sfmm.h- Header file with structure definitionsdebug.h- Debugging utilities- Test harness for validation
- Not thread-safe (requires external synchronization for concurrent access)
- No support for alignment requirements beyond 16 bytes
- Fixed heap growth policy (one page at a time)
- Based on concepts from Computer Systems: A Programmer's Perspective (CS:APP)
- Uses boundary tags (headers/footers) for constant-time coalescing
- Segregated free lists reduce search time compared to single free list
- Quick lists trade memory for speed in common allocation patterns
- Thread-local caches for concurrent allocation
- Better heap growth heuristics
- Support for custom alignment requirements
- Memory defragmentation strategies