AdvancedMemoryManager is a focused C toolkit for aligned allocations, reusable memory pools, arena-style scratch allocation, lightweight reference counting, tagged allocations, and allocator diagnostics. The project started as a compact learning exercise and is now being shaped into something much more practical: a small, readable allocator toolkit that is safe to study, easy to benchmark, and straightforward to extend.
Most memory manager examples online stop right where things become interesting. They show an allocation trick or a toy free list, but they rarely keep enough metadata around to make free, realloc, reporting, and pool reuse behave like a coherent system.
This repository is meant to close that gap.
The current toolkit is built around a few principles:
- Keep ownership explicit.
- Make aligned allocation correct, not approximate.
- Reuse memory pools when they help, and fall back to the heap when they do not.
- Offer a fast arena path for short-lived scratch allocations.
- Expose useful runtime statistics instead of hiding allocator behavior.
- Make it easy to see which subsystem or workload is actually consuming memory.
- Surface misuse early with lightweight debug hooks instead of silent failure.
- Keep the code readable enough that someone can learn from it without fighting the implementation.
- Aligned allocation with raw-pointer tracking, so release and resize paths stay correct.
- Fixed-size memory pools for predictable reuse on common allocation sizes.
- Arena allocation for frame-based, request-scoped, or scratch-buffer workloads.
- Heap fallback for requests that do not match an existing pool.
- Reference counting for shared allocations that need explicit retain and release semantics.
- Runtime statistics for active bytes, peak usage, pool hits, heap hits, failed allocations, and invalid ownership operations.
- Pool inspection helpers for diagnostics and tooling.
- Debug hooks for invalid retain, release, and realloc usage.
- Faster active-allocation bookkeeping through an internal pointer index and constant-time unlinking for live blocks.
- Tagged allocations and tag snapshots for lightweight subsystem-level profiling.
- A demo program that exercises the public API end to end.
- A small test suite that checks alignment, pool roundtrips, arena reset behavior, reallocation, zeroed allocation, reference counting, and misuse detection.
- A benchmark target for comparing
malloc/free, pool-backed allocations, and arena resets.
.
├── include/
│ └── advanced_memory_manager.h
├── src/
│ └── advanced_memory_manager.c
├── benchmarks/
│ └── benchmark_allocators.c
├── tests/
│ └── test_memory_manager.c
├── Makefile
└── mem_manager.c
include/advanced_memory_manager.h: public API for the allocator toolkitsrc/advanced_memory_manager.c: allocator core, metadata handling, pools, and reportingbenchmarks/benchmark_allocators.c: rough performance comparison between standard allocation, pool reuse, and arena resetstests/test_memory_manager.c: validation for the most important behaviorsmem_manager.c: a small demo entrypoint that shows how the toolkit is meant to be used
Build the demo and run the tests:
make allBuild only the demo:
make demoRun the test suite:
make testRun the tests with sanitizers:
make sanitizeRun the benchmark target:
make benchmarkClean generated binaries:
make clean#include "advanced_memory_manager.h"
int main(void) {
AmmManager* manager = amm_create();
int* values;
if (manager == NULL) {
return 1;
}
amm_add_pool(manager, 64, 16, 16);
values = (int*)amm_alloc(manager, 8 * sizeof(int), 16);
if (values != NULL) {
values = (int*)amm_realloc(manager, values, 16 * sizeof(int), 16);
amm_release(manager, values);
}
amm_destroy(manager);
return 0;
}Core lifecycle:
amm_createamm_destroy
Pool management:
amm_add_poolamm_get_pool_countamm_get_pool_stats
Arena lifecycle:
amm_arena_createamm_arena_allocamm_arena_resetamm_arena_get_statsamm_arena_destroy
Allocation helpers:
amm_allocamm_alloc_taggedamm_callocamm_calloc_taggedamm_reallocamm_realloc_taggedamm_cloneamm_clone_tagged
Ownership and validation:
amm_retainamm_releaseamm_contains
Diagnostics:
amm_get_statsamm_get_tag_countamm_get_tag_snapshotamm_print_reportamm_set_debug_hookamm_arena_print_report
The old single-file version was honest about its limitations, but it still had a few structural issues that kept it in demo territory:
- aligned allocations did not retain the original raw pointer
- release and resize flows could not be fully correct
- pool ownership was inferred indirectly instead of stored directly
- diagnostics existed, but the internals were not trustworthy enough yet
The new version fixes those foundations first, then layers arena allocation, debug hooks, and benchmarks on top of them. That order matters more than adding clever allocator tricks on top of a shaky base.
The arena API is intentionally simple. It is designed for temporary memory where allocations move forward quickly and the entire region can be reset in one shot.
That makes it a good fit for:
- per-frame data in small engines and visual tools
- request-scoped scratch memory
- parser or serializer work buffers
- temporary staging memory in benchmarks
- allocation profiling by subsystem when combined with allocator tags
It is not intended for general-purpose ownership graphs. If memory needs independent release semantics, the manager and pool APIs are the better fit.
The debug hook is there to catch suspicious allocator use while the program is still easy to reason about. Right now it reports:
- invalid retain attempts
- invalid release attempts
- invalid realloc attempts
- pool creation failures
The hook is intentionally lightweight. It gives you a place to log, count, or escalate allocator misuse without forcing a heavyweight logging framework into the library.
Tagged allocations are meant for the moment when raw allocator totals stop being enough. Knowing that memory is active is useful. Knowing that the bytes belong to cache, parser, scene, frame, or network is much more actionable.
The tagging layer stays intentionally small:
amm_alloc_taggedamm_calloc_taggedamm_realloc_taggedamm_clone_taggedamm_get_tag_countamm_get_tag_snapshot
If a tag is not provided, the allocator falls back to untagged.
This is not meant to replace a full observability stack. It is meant to make day-to-day allocator profiling far easier while keeping the library compact.
The benchmark target is meant to be comparative, not absolute. It is useful for checking trends on your machine after changes to the allocator, but it should not be treated as a universal truth about allocator performance.
On the current sandbox run, the rough numbers came out like this:
malloc/free: about312 ms- pool path: about
831 ms - arena path: about
118 ms
That result is a useful reminder that a small allocator toolkit still needs measurement and iteration. The arena path already shines for reset-heavy workloads, and the pool path improved substantially after the manager-side lookup and active-list unlink optimizations, but it still has room to improve.
This repository can become a genuinely strong systems-programming toolkit if it keeps growing in the right order. The next useful upgrades are:
- pool fast-path improvements so benchmarks better reflect the intended design
- configurable debug hooks for stricter policies, including abort-on-misuse modes
- optional thread-safe wrappers for shared runtime environments
- richer diagnostics export for CLI tools or dashboards
- learning how allocator metadata is designed in real code
- experimenting with aligned allocation strategies
- building fixed-size pool allocators for embedded or performance-sensitive work
- profiling allocation patterns in small systems projects
- using a compact C codebase for interviews, workshops, or systems exercises
The goal is not to become a drop-in replacement for industrial allocators overnight. The goal is to become one of the clearest and most useful allocator toolkits in its size class: small enough to read in an evening, solid enough to trust in experiments, and structured well enough to keep improving without turning into a mess.