Capstone Project 5

Memory Manager

Build a custom memory allocator in C that implements dynamic memory allocation, memory leak detection and prevention, and pool allocation patterns. You'll understand how malloc/free work under the hood and create debugging tools to track memory usage and identify leaks.

15-20 hours
Advanced
500 Points
What You Will Build
  • Custom malloc/free implementation
  • Memory leak detector
  • Pool allocator pattern
  • Memory usage statistics
  • Debug memory visualizer
Contents
01

Project Overview

This advanced capstone project focuses on low-level memory management in C. You will build a custom memory allocator that mimics the behavior of malloc/free while adding powerful debugging and leak detection capabilities. This project will give you deep insight into how heap memory works, memory fragmentation, and best practices for preventing memory leaks.

Skills Applied: This project tests your proficiency in pointer arithmetic, linked data structures, bit manipulation, memory layout understanding, debugging techniques, and systems programming in C.
Dynamic Allocation

Implement custom malloc, calloc, realloc, and free

Leak Detection

Track allocations and report unreleased memory

Pool Allocator

Fixed-size block allocation for performance

Statistics

Track usage, fragmentation, and allocation patterns

Learning Objectives

Technical Skills
  • Understand heap memory layout and management
  • Implement free list data structures
  • Master pointer arithmetic and alignment
  • Handle memory fragmentation strategies
  • Build debugging and profiling tools
Systems Programming Skills
  • Understand how malloc/free work internally
  • Learn memory allocation strategies (first-fit, best-fit)
  • Identify and prevent common memory bugs
  • Profile memory usage in applications
  • Optimize allocation performance
Ready to submit? Already completed the project? Submit your work now!
Submit Now
02

Project Scenario

SystemCore Technologies

You have been hired as a Systems Programmer at SystemCore Technologies, a company that develops embedded systems and performance-critical applications. The team has been experiencing memory-related bugs in production, including memory leaks that cause system crashes after extended operation. Your task is to build a custom memory management library with built-in debugging capabilities.

"Our production systems are crashing due to memory leaks we can't trace. We need a custom allocator that tracks every allocation, detects leaks, and provides memory usage statistics. Can you build a drop-in replacement for malloc that helps us find and fix these issues?"

Sarah Chen, Lead Systems Engineer

Technical Challenges to Solve

Memory Allocation
  • Implement efficient block allocation from heap
  • Handle variable-size allocation requests
  • Manage free block list for reuse
  • Ensure proper memory alignment (8/16 byte)
Leak Detection
  • Track all allocations with source file and line
  • Detect memory that was never freed
  • Report leak locations at program exit
  • Calculate total leaked bytes
Pool Allocation
  • Pre-allocate fixed-size memory blocks
  • O(1) allocation and deallocation
  • Reduce fragmentation for same-size objects
  • Support multiple pool sizes
Statistics & Debug
  • Track current/peak memory usage
  • Count allocations and frees
  • Detect double-free and use-after-free
  • Visualize memory block layout
Pro Tip: Think like a systems programmer! Your memory manager should be a drop-in replacement that developers can use simply by including your header and using your functions instead of malloc/free.
03

The Dataset

You will work with test programs and allocation pattern files for testing your memory manager. Download the files containing various allocation scenarios for comprehensive testing:

Dataset Download

Download the Amazon Fine Food Reviews dataset from Kaggle (568K reviews, 300.9 MB) to stress-test your memory manager with variable-length strings and realistic allocation patterns.

Original Data Source

This project uses the Amazon Fine Food Reviews dataset from Kaggle - a collection of 568,454 food reviews from Amazon spanning 10+ years. This dataset is ideal for stress-testing your memory allocator with variable-length strings, nested structures, and heavy allocation/deallocation patterns.

Dataset Info: 568,454 reviews × 10 columns | File Size: 300.9 MB (Reviews.csv) | Total: 673.7 MB (3 files) | Columns: Id, ProductId, UserId, ProfileName, HelpfulnessNumerator, HelpfulnessDenominator, Score, Time, Summary, Text | 74,258 products | 256,059 users
Dataset Schema

ColumnTypeDescriptionUnique Values
IdIntegerRow Id (unique identifier)568,454
ProductIdStringUnique identifier for the product74,258
UserIdStringUnique identifier for the user256,059
ProfileNameStringProfile name of the user218,418
HelpfulnessNumeratorIntegerNumber of users who found the review helpful0-866
HelpfulnessDenominatorIntegerNumber of users who indicated if review was helpful or not0-1047
ScoreIntegerRating between 1 and 51-5
TimeIntegerTimestamp for the review (Unix time)-
SummaryStringBrief summary of the reviewVariable
TextStringFull text of the reviewVariable

SQLite database containing the same Reviews table for direct SQL queries and testing.

Usage: Load records using SQLite queries to test your memory allocator with database-style access patterns and batch allocations.

Contains MD5/SHA hashes for verifying file integrity after download.

Dataset Stats: 568,454 reviews with variable-length text fields for stress-testing allocators
Verification: Process all reviews with zero memory leaks and accurate allocation tracking
04

Key Concepts

Before implementing the memory manager, make sure you understand these fundamental concepts:

Memory Block Structure

Each allocated block needs metadata (header):

typedef struct block_header {
    size_t size;           // Block size
    int is_free;           // 0 = in use, 1 = free
    struct block_header *next;
    struct block_header *prev;
    // Debug info
    const char *file;
    int line;
} block_header_t;

Important: User gets pointer after header. Account for header size in total allocation. Ensure proper alignment.

Free List Management

Track free blocks using a linked list:

// Allocation strategies:
// First-fit: Use first block that fits
// Best-fit: Find smallest suitable block
// Worst-fit: Find largest block (reduces fragmentation)

void *my_malloc(size_t size) {
    block_header_t *block = find_free_block(size);
    if (block) {
        split_block_if_needed(block, size);
        block->is_free = 0;
        return (void*)(block + 1);
    }
    return request_more_memory(size);
}
Memory Alignment

Ensure returned pointers are properly aligned:

// Align to 8 or 16 byte boundary
#define ALIGNMENT 16
#define ALIGN(size) \
    (((size) + (ALIGNMENT-1)) & ~(ALIGNMENT-1))

// Example: ALIGN(17) = 32 (next 16-byte boundary)
// Example: ALIGN(16) = 16 (already aligned)

Why alignment? CPU access to unaligned memory is slower or may cause crashes on some architectures. Always align to at least 8 bytes.

Pool Allocator Pattern

Pre-allocate fixed-size blocks for O(1) allocation:

typedef struct pool {
    size_t block_size;    // Size of each block
    size_t pool_size;     // Total pool capacity
    void *memory;         // Contiguous memory
    void *free_list;      // Stack of free blocks
} pool_t;

pool_t *pool_create(size_t block_size, size_t count);
void *pool_alloc(pool_t *pool);
void pool_free(pool_t *pool, void *ptr);

Benefit: No fragmentation, constant-time allocation. Perfect for objects of the same size (nodes, particles, etc.).

05

Project Requirements

Your project must implement the following features. Focus on correctness first, then add bonus features.

1
Dynamic Memory Allocation (Required)
  • void *my_malloc(size_t size) - Allocate memory block
  • void my_free(void *ptr) - Free allocated memory
  • void *my_calloc(size_t count, size_t size) - Allocate and zero-initialize
  • void *my_realloc(void *ptr, size_t new_size) - Resize allocation
  • Implement first-fit or best-fit allocation strategy
  • Coalesce adjacent free blocks to reduce fragmentation
2
Leak Detection (Required)
  • Track all allocations with source file name and line number
  • Use macros to capture __FILE__ and __LINE__
  • Provide mem_check_leaks() function to report unreleased memory
  • Print leak report: address, size, allocation location
  • Calculate and display total leaked bytes
  • Detect double-free attempts and report error
3
Pool Allocator (Required)
  • pool_t *pool_create(size_t block_size, size_t count)
  • void *pool_alloc(pool_t *pool) - O(1) allocation
  • void pool_free(pool_t *pool, void *ptr) - O(1) deallocation
  • void pool_destroy(pool_t *pool) - Free entire pool
  • Handle pool exhaustion gracefully (return NULL or expand)
  • Validate that freed pointers belong to the pool
4
Statistics & Debugging (Required)
  • mem_stats_t mem_get_stats() - Return usage statistics
  • Track: current usage, peak usage, allocation count, free count
  • Calculate fragmentation percentage
  • void mem_dump() - Print memory block layout
  • Optional: ASCII visualization of memory blocks
5
Bonus Features (Optional)
  • Use-after-free detection: Mark freed memory with pattern, detect access
  • Buffer overflow detection: Add guard bytes, check on free
  • Memory profiler: Track allocation hot spots by call site
  • Thread safety: Add mutex locks for multi-threaded use
  • Best-fit allocator: Implement best-fit in addition to first-fit
06

Submission Requirements

Create a public GitHub repository with the exact name shown below:

Required Repository Name
c-memory-manager
github.com/<your-username>/c-memory-manager
Required Project Structure
include/
  • mymalloc.h
  • pool_alloc.h
  • mem_debug.h
  • mem_stats.h
src/
  • mymalloc.c
  • pool_alloc.c
  • mem_debug.c
  • mem_stats.c
  • free_list.c
Other
  • tests/ (test programs)
  • data/ (allocation patterns)
  • Makefile
  • README.md
README.md Required Sections
1. Project Header
  • Project title and description
  • Your full name and submission date
  • Course and project number
2. Features
  • List of implemented allocator functions
  • Leak detection capabilities
  • Pool allocator features
3. Build Instructions
  • Prerequisites (GCC, make)
  • How to compile with make
  • How to run tests
4. API Documentation
  • Function signatures and descriptions
  • Usage examples for each function
  • Error handling behavior
5. Implementation Details
  • Allocation strategy used
  • Block header structure
  • How leak detection works
6. Testing
  • How to run the test suite
  • Test coverage explanation
  • Performance benchmarks
07

Grading Rubric

Your project will be evaluated on the following criteria (500 points total):

Category Criteria Points
Dynamic Allocation malloc works correctly for various sizes 40
free releases memory properly 30
realloc handles resize correctly 25
Block coalescing implemented 25
Leak Detection Tracks allocations with file:line 40
Reports all leaks on check 30
Detects double-free errors 20
Pool Allocator pool_create and pool_destroy work 30
O(1) allocation and free 30
Handles pool exhaustion 20
Statistics Accurate usage tracking 25
Memory dump visualization 15
Code Quality Modular structure (separate files) 25
Clear comments and documentation 20
No memory leaks in allocator itself 25
Documentation README completeness 40
API documentation quality 20
Bonus Use-after-free, buffer overflow, thread safety +50
Total 500 (+50 bonus)
Excellent
450+

Exceeds all requirements with exceptional quality

Good
375-449

Meets all requirements with good quality

Satisfactory
300-374

Meets minimum requirements

Needs Work
< 300

Missing key requirements

Ready to Submit?

Make sure you have completed all requirements and reviewed the grading rubric above.

Submit Your Project
08

Pre-Submission Checklist

Use this checklist to verify you have completed all requirements before submitting your project.

Dynamic Allocation
Leak Detection
Pool Allocator
Repository Requirements
Final Check: Run your allocator through valgrind to ensure your allocator itself has no memory leaks: valgrind --leak-check=full ./test
09

Common Issues and Solutions

Encountering problems? Don't worry! Here are the most common issues students face and how to resolve them quickly.

Alignment Issues
Problem

Segfault or bus error when accessing allocated memory

Solution

Ensure returned pointers are aligned to 8 or 16 bytes:

#define ALIGN(x) (((x) + 15) & ~15)
Fragmentation Problems
Problem

malloc fails even though sufficient free memory exists

Solution

Implement block coalescing - merge adjacent free blocks:

if (block->next && block->next->is_free) coalesce(block);
Corrupted Free List
Problem

Infinite loop or crash when traversing free list

Solution

Carefully update prev/next pointers. Draw diagrams to verify:

block->prev->next = block->next;
block->next->prev = block->prev;
Size Calculation Errors
Problem

Allocated blocks overlap or header gets overwritten

Solution

Always account for header size in total block size:

total = ALIGN(size + sizeof(block_header_t));
Macro Capture Issues
Problem

__FILE__ and __LINE__ show wrong location

Solution

Use macro wrapper that captures at call site:

#define my_malloc(s) my_malloc_debug(s, __FILE__, __LINE__)
Pool Allocator Bugs
Problem

Pool runs out of blocks or returns invalid pointers

Solution

Initialize free list as stack, validate pointers on free:

if (ptr >= pool->start && ptr < pool->end) {...}
Still Having Issues?

Check the course discussion forum or reach out for help