A custom memory allocator written in C that implements malloc, free, and realloc semantics using a hybrid arena + bin strategy:
- Bin-based recycling for small allocations (≤ 320 bytes) — O(1) free-list per size class.
- Arena bump-pointer for large allocations — linear carving from a contiguous region grown on demand.
- Free-block coalescing to reduce fragmentation over time.
Platform: Linux / POSIX and Windows. Heap growth uses
sbrk(2)on POSIX systems andVirtualAllocon Windows.
| File | Purpose |
|---|---|
allocator.h |
Public API — structs, constants, and function prototypes |
allocator.c |
Implementation of the allocator |
main.c |
Usage demonstration and basic correctness tests |
Makefile |
Build system |
initialize_arena(size) bootstraps the allocator by requesting a page-aligned memory region from the OS (sbrk(2) on POSIX, VirtualAlloc on Windows). All subsequent allocations are served from this region until it is exhausted, at which point a new page-aligned region is requested automatically.
Allocations up to BIN_COUNT × BIN_SIZE bytes (10 × 32 = 320 bytes) are routed to one of 10 size-class free-lists. When a chunk is freed, it is prepended to its bin's list and reused immediately by the next matching allocation — this avoids touching the arena again.
Size range Bin index
----------- ---------
1 – 32 bytes 0
33 – 64 bytes 1
65 – 96 bytes 2
… …
289 – 320 bytes 9
Requests above 320 bytes are carved directly from the arena. A Chunk header is written immediately before the returned pointer, storing the payload size for use by custom_free and custom_realloc.
merge_free_blocks() scans every bin free-list for physically contiguous chunk pairs and merges them. This reduces fragmentation when many same-sized blocks are freed and then needed in larger sizes.
Requires gcc and make — on Linux natively, or on Windows via MinGW-w64 (e.g. MSYS2).
# Clone
git clone https://github.com/<your-username>/MALLOC.git
cd MALLOC
# Build release binary
make
# Run the demonstration
make run
# Build with AddressSanitizer + UBSan (recommended for development)
make debug
./allocator
# Clean build artefacts
make cleanNote:
make debugrequires AddressSanitizer support, which MinGW gcc on Windows generally lacks. Use WSL/Linux (or clang with ASan) for sanitized builds.
#include "allocator.h"
#include <stdio.h>
int main(void)
{
/* Initialise a 64 KiB arena */
if (initialize_arena(16 * PAGE_SIZE) != 0)
{
fprintf(stderr, "Failed to initialise arena.\n");
return 1;
}
void *ptr1 = custom_malloc(50); /* served from bin[1] */
void *ptr2 = custom_malloc(400); /* served from arena */
void *ptr3 = custom_malloc(50); /* served from bin[1] */
custom_free(ptr1);
custom_free(ptr3);
/* Reallocate ptr2 from 400 -> 800 bytes */
ptr2 = custom_realloc(ptr2, 800);
custom_free(ptr2);
merge_free_blocks();
return 0;
}Initialises the allocator with a heap region of at least arena_size bytes. Must be called once before any other function. Returns 0 on success, -1 on failure.
Allocates size bytes. Returns NULL if size == 0 or on allocation failure.
Frees a previously allocated block. No-op when ptr is NULL.
Resizes the allocation at ptr to size bytes, preserving existing data. Behaves like custom_malloc(size) when ptr is NULL, and like custom_free(ptr) when size is 0.
Coalesces adjacent free chunks within each bin to reduce fragmentation.
- Large allocations are not reclaimed on
custom_free—sbrkdoes not support arbitrary frees. A production implementation would usemmap/munmap(orVirtualFreeon Windows) instead. - When the arena is exhausted, the allocator abandons the remainder of the old region and switches to a fresh one; the leftover bytes are never reused.
- The allocator is not thread-safe. Concurrent use requires external locking.
initialize_arenamay only be called once per process lifetime in the current implementation.
This project is released under the MIT License.