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.
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
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?"
Technical Challenges to Solve
- Implement efficient block allocation from heap
- Handle variable-size allocation requests
- Manage free block list for reuse
- Ensure proper memory alignment (8/16 byte)
- Track all allocations with source file and line
- Detect memory that was never freed
- Report leak locations at program exit
- Calculate total leaked bytes
- Pre-allocate fixed-size memory blocks
- O(1) allocation and deallocation
- Reduce fragmentation for same-size objects
- Support multiple pool sizes
- Track current/peak memory usage
- Count allocations and frees
- Detect double-free and use-after-free
- Visualize memory block layout
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 Schema
| Column | Type | Description | Unique Values |
|---|---|---|---|
Id | Integer | Row Id (unique identifier) | 568,454 |
ProductId | String | Unique identifier for the product | 74,258 |
UserId | String | Unique identifier for the user | 256,059 |
ProfileName | String | Profile name of the user | 218,418 |
HelpfulnessNumerator | Integer | Number of users who found the review helpful | 0-866 |
HelpfulnessDenominator | Integer | Number of users who indicated if review was helpful or not | 0-1047 |
Score | Integer | Rating between 1 and 5 | 1-5 |
Time | Integer | Timestamp for the review (Unix time) | - |
Summary | String | Brief summary of the review | Variable |
Text | String | Full text of the review | Variable |
SQLite database containing the same Reviews table for direct SQL queries and testing.
Contains MD5/SHA hashes for verifying file integrity after download.
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.).
Project Requirements
Your project must implement the following features. Focus on correctness first, then add bonus features.
Dynamic Memory Allocation (Required)
void *my_malloc(size_t size)- Allocate memory blockvoid my_free(void *ptr)- Free allocated memoryvoid *my_calloc(size_t count, size_t size)- Allocate and zero-initializevoid *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
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
Pool Allocator (Required)
pool_t *pool_create(size_t block_size, size_t count)void *pool_alloc(pool_t *pool)- O(1) allocationvoid pool_free(pool_t *pool, void *ptr)- O(1) deallocationvoid pool_destroy(pool_t *pool)- Free entire pool- Handle pool exhaustion gracefully (return NULL or expand)
- Validate that freed pointers belong to the pool
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
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
Submission Requirements
Create a public GitHub repository with the exact name shown below:
Required Repository Name
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
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
Exceeds all requirements with exceptional quality
Good
Meets all requirements with good quality
Satisfactory
Meets minimum requirements
Needs Work
Missing key requirements
Ready to Submit?
Make sure you have completed all requirements and reviewed the grading rubric above.
Submit Your ProjectPre-Submission Checklist
Use this checklist to verify you have completed all requirements before submitting your project.
Dynamic Allocation
Leak Detection
Pool Allocator
Repository Requirements
valgrind --leak-check=full ./test
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
Segfault or bus error when accessing allocated memory
Ensure returned pointers are aligned to 8 or 16 bytes:
#define ALIGN(x) (((x) + 15) & ~15)
Fragmentation Problems
malloc fails even though sufficient free memory exists
Implement block coalescing - merge adjacent free blocks:
if (block->next && block->next->is_free) coalesce(block);
Corrupted Free List
Infinite loop or crash when traversing free list
Carefully update prev/next pointers. Draw diagrams to verify:
block->prev->next = block->next;
block->next->prev = block->prev;
Size Calculation Errors
Allocated blocks overlap or header gets overwritten
Always account for header size in total block size:
total = ALIGN(size + sizeof(block_header_t));
Macro Capture Issues
__FILE__ and __LINE__ show wrong location
Use macro wrapper that captures at call site:
#define my_malloc(s) my_malloc_debug(s, __FILE__, __LINE__)
Pool Allocator Bugs
Pool runs out of blocks or returns invalid pointers
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