Custom Heap Allocator

Heap profile picture

The goal of this operating systems project was to build a custom dynamic memory allocator by implementing malloc, free and realloc using explicit free lists. My allocator featured block headers and footers for metadata, coalescing of adjacent free blocks, and optimizations to balance space utilization and throughput. This project was written in C, developed in a Linux environment, and I relied on GDB for debugging.

Visual representation of the heap structure after calls to malloc and free

Implementation

My implementation focused on writing clean, modular code that could be modified as I added performance improvements. I started with a basic heap and block structures, defining metadata using a series of pointers, bit masks and pointer arithmetic. At this stage, I used a single implicit free list to find free blocks, which was simple but hurt throughput. However, I did implement coalescing to merge adjacent free blocks which improved space utilization. From there, I was able to switch to a single explicit free list using a doubly linked list, followed by segregated free lists grouped by size ranges. This immensely improved throughput by limiting the search space for allocations. I also added footer optimization which allowed more payload space to be available for users. These small improvements were manageable because of my use of simple, single-purpose functions that follow the Single Responsibility Principle.

Examples of clean, modular code

Challenges

This project became one of my biggest lessons in resilience as a programmer. Full transparency: I needed to complete this project twice because I failed my first attempt. My original code had so many bugs, and I realized I did not know how to debug or read my own code hours after writing it. After days of looking over my code with TA’s, I could not get it working in time. On my second attempt, I approached the project differently using what I learned. I started by organizing my code like I mentioned above. Then it came down to two things, problem solving through visualization and knowing how to debug.

Impact

One of the most valuable habits I developed was drawing functions and memory structures on paper before coding them. Visualizing how blocks would split, coalesce, or move through free lists gave me a clearer picture of what my functions should do. This made implementing and checking my logic easier. I still use this approach in almost every project.

Once implemented, knowing how to debug is what led me to success on my second attempt. I used GDB to walk me through entire trace files, following function calls and variable changes line by line. Even when my allocator worked correctly, I analyzed the program flow to build intuition for how different parts interacted. This habit allowed me to catch subtle issues early. Since then, debugging has become a major strength. I am able to debug my own problems more efficiently, and often help fellow programmers fix their projects.

The project concluded with a presentation to my professor, where I demonstrated my allocator and a heap checker I wrote to search for inconsistencies. I received a 100% on the presentation and an A overall.

Other Projects

Project 1

Student Tracking
Web App

Project 3

AI Sudoku
Solver

Arrow icon