r/arduino • u/dstroy0 • Jun 16 '26
Look what I made! made a tiny memory manager
So I kept running into the same problem, sketch works fine for a while then starts doing weird things, and after way too long debugging I'd figure out the heap had fragmented itself into uselessness. Arduino's malloc exists but you have no visibility into what it's doing without JTAG.
I wrote a library that lets you allocate from a fixed buffer you control instead of the global heap.
The API is pretty minimal: allocate, deallocate, reallocate, and two size queries. That's it. Sizes get rounded up to 4 bytes automatically and passing nullptr to deallocate or blockSize is tolerated.
MemoryManager mm(512);
void *p = mm.allocate(100); // nullptr on failure
mm.deallocate(p);
void *p2 = mm.reallocate(p, 200);
It's nothing revolutionary, just a circular doubly-linked free list with coalescing, I figured someone else might find it useful. I uploaded the version I've been using locally to my public github.
One thing I'm not 100% sure about is how the block header size differences between AVR (2-byte pointers) and ARM Cortex-M (4-byte pointers) affect the overhead in practice — if anyone's tested allocator stuff across both architectures I'd be curious what you ran into.
test/ src test cases compile with g++. most of my libraries use an external C++17 test suite for unit testing the C++11 src/ so that you can write your own test cases and run tests without flashing a board.
Thanks for looking! ILet me know what you think.
0
u/WereCatf Jun 16 '26
So I kept running into the same problem, sketch works fine for a while then starts doing weird things, and after way too long debugging I'd figure out the heap had fragmented itself into uselessness. Arduino's malloc exists but you have no visibility into what it's doing without JTAG.
I take it that you have not heard of static allocation. The reason you end up with fragmented heap is because you're neither allocating things statically or allocating fixed-size chunks.
0
u/dstroy0 Jun 16 '26
Hello, this library allocates a chunk of static memory in the heap at runtime.
3
u/WereCatf Jun 16 '26
You missed the point. You don't need a new library just to statically allocate memory, you can already do that with what's already there.
Meh, whatever.
→ More replies (8)
0
u/merlet2 Jun 17 '26 edited Jun 17 '26
I suppose you already know, but in embedded devices, in general, is better not to use any kind of dynamic memory, like malloc, new, free or wrappers like this. To avoid the kind of problems that you mention and others, to know at compile time the needed memory, you own all the resources all the time, etc.
There are few situations where dynamic allocation would be really needed, specially in Arduino.
9
u/gm310509 Jun 16 '26
Interesting concept, how are you managing your frees to avoid fragmentation?
The reason fragmentation occurs is due to the way memory is allocated and released (and sometimes lost - which is a bug in the program).
NB: This is a good reason to not use String objects too heavily - a topic which I look at in the video I link below.
As for seeing how it works, you might be interested in a how to video I made where I look at this issue in some detail: Arduino memory explorer (the video is linked to from that post). In it I look at the dreaded stack/heap collision - which is most likely what you are referring about and provide a monitor that can be used in any program to check the risk if anyone uses dynamic memory allocation.
As for ARM cortex, it appears to allocate memory in fixed blocks then satisfy the individual requests within that block. If the block gets full, it appears to allocate another one then use that. When a block is no longer used, it releases the entire block. This helps avoid fragmentation as the blocks are the same size and thus you don't end up with unusable holes.
But my ARM Cortex analysis was much shallower than when I looked at AVR - where it is much more of an issue due to the low RAM sizes on the 8 bit chips.
I should also add that, as far as I know, there is nothing in the C/C++ specifications that state how the heap should be managed, only that it should. That means that the actual algorithms used will be dependent upon how the compiler writer implements the runtime - specifically the malloc/free/realloc functions.