What Building malloc Taught Me About Memory
Table of Contents

Modern developers rarely think about memory.
We call malloc, new, or let a garbage collector handle everything. Most of the time that’s perfectly fine. But recently I decided to dig deeper and implement a very small version of malloc myself.
Not to replace real allocators. Just to understand what actually happens under the hood.
The result was surprisingly simple and incredibly educational.
This post walks through the core ideas.
Why Understanding Memory Still Matters
Most software today runs on layers of abstraction.
Frameworks → languages → runtimes → operating systems.
That’s powerful, but it also means many developers never see the foundations those layers are built on.
Understanding memory allocation helps with:
• writing faster software
• debugging memory issues
• understanding how runtimes work
• building systems tools
• designing better APIs
Even if you work in higher level languages like Go , these concepts still power everything underneath.
Step 1: Where malloc Gets Memory
When a program runs, memory roughly looks like this:
Code
Globals
Heap ← grows upward
Stack ← grows downward
malloc allocates from the heap.
Under the hood, the allocator asks the OS for more heap space using a system call like sbrk().
Example:
void *ptr = sbrk(1024);
This moves the program break forward by 1024 bytes and returns a pointer to the new memory.
Think of it like extending the end of your program’s heap.
Step 2: The Naive Allocator
The simplest possible allocator would just call sbrk.
void *my_malloc(size_t size) {
return sbrk(size);
}
But this immediately creates a problem.
If you later call free, the allocator has no idea :
• how big the allocation was
• where the next block starts
• whether a block can be reused
So real allocators store metadata.
Step 3: The Hidden Header Trick
Every allocation secretly stores a small header before the memory returned to the user.
Memory layout:
[ HEADER ][ USER MEMORY ]
The header might contain:
size
is_free
next_block
Example structure:
typedef struct block_header {
size_t size;
int is_free;
struct block_header *next;
} block_header_t;
When the allocator returns memory, it actually returns a pointer after the header.
return (void *)(block + 1);
Which means the user sees:
ptr → usable memory
While the allocator still has its metadata.
Step 4: Tracking Allocations
Blocks are usually tracked using a linked list.
heap_start
↓
[block A] → [block B] → [block C]
When malloc runs:
- search for a free block big enough
- reuse it if possible
- otherwise request more memory from the OS
This keeps allocations fast and avoids unnecessary system calls.
Step 5: What free Actually Does
One surprising thing: free usually does not return memory to the OS.
Instead it just marks the block as reusable.
[HEADER free=1][memory]
Future allocations can reuse that block instead of requesting more memory.
What Real Allocators Do Differently
Real allocators (like the ones used by glibc) add several optimizations:
Block splitting
Free block: 100 bytes
Need: 20
Split into:
20 used
80 still free
Block coalescing
Adjacent free blocks merge together to reduce fragmentation.
Large allocation handling
Large allocations often use mmap instead of the heap.
Thread safety
Modern allocators maintain thread local arenas for performance.
What I Learned From This
Building even a tiny allocator reinforced a few important lessons.
Abstractions hide complexity, but the fundamentals still matter
Languages like Go make memory management easier, but the underlying principles are still there.
Performance often comes from understanding the layers below you
Understanding memory layout and allocation patterns helps explain why some code performs better than others.
Systems knowledge compounds
Once you understand memory allocators, many other topics suddenly make more sense:
• garbage collectors
• runtime design
• kernel memory management
• high performance servers
Final Thoughts
You don’t need to write your own allocator for production software.
But building one, even a small version, forces you to think like the runtime.
And that perspective is valuable.
The more you understand the layers beneath your tools, the better engineer you become.
GITHUB => https://github.com/tacheraSasi/malloc.git
Originally published on Medium.