Hi there! đ
First things first - I promised more articles this week but I barely managed to deliver two so far. I apologize. The thing is, if you care, that I had three conference talks to give (Wednesday, Thursday and Friday) and I simply presumed that I will manage to publish the articles in the night-hours. Well, what I didnât count on was my sonâs teething going crazy, virtually leaving us with no sleep at all :) So yep, I barely pulled this one and Iâll try pushing through one more on Sunday!
But thatâs not what I wanted to talk about though. What I want to discuss today is Windowsâ Heap API. That magical thing that pretty much ANY language runtime eventually has to interact with. Be it Câs âmalloc()â, C++âs ânewâ, Rust or .NET - they all interact with Windows via this API. So, letâs discuss it.
As usual, the bitesized graphic first and the more detailed explanation will follow below.
I hope that infographic gave you a clue? :)
One thing that you should be aware of is that the moment your process starts, you get 1MB of Stack and 1MB of Heap memory. What that further means is that if you can fit your Heap requirements in that 1MB, you should, theoretically, end up being way more performant than if you kept asking for more memory.
Whatâs cool to know is that you can override this behavior by providing the /LINK directive to linker. If you know that your process will require, say, 5MBs to operate at itâs finest, then you should be good to go and ask for 5MB from the start.
What I find even more interesting is the sole reason of why would anyone use HeapAlloc instead of ânewâ or âmalloc()â. And frankly, you probably shouldnât, because default implementations are good enough for most cases.
HOWEVER, and this is really cool, by using Windowsâ native API for memory allocation you get access to some Windows-specific functionalities which simply can not be provided by generic implementations like ânewâ and âmalloc()â. And two coolest things that you can do are:
Create private Heaps, and
Pass some low-level flags (e.g. in order to disable serialization)
You might be wondering why in the world would anyone want to have more than one heap, right? Isnât one enough?
One is enough. But having private ones really unlocks some cool things that I already mentioned in the graphic:
You can create Memory Pools (i.e. heaps of fixed size) and allocate memory from there. Furthermore, you can overload the operator new and place only specific objects on that private Heap, giving you a full control over WHERE they are and ensuring that you can remove them in one go by simply calling HeapDestroy().
Another cool thing you can do is have one Heap per thread. And if you are certain your heaps wonât need to share memory, thereâs no need to have them wait for memory allocations (you are probably aware that ânewâ and âmalloc()â are thread-safe and serialized, right?). By creating private Heaps and using the HEAP_NO_SERIALIZE flag you can ensure that each thread gets its bits immediately and without overlaps with others.
And finally, by allocating memory upfront, you can prevent memory fragmentation from happening. One typical example would be if you had a dynamically growing linked-list. The more time it passes between elements being added, the higher the chances are of those memory chunks ending up on different memory pages.
Obviously, thereâs a downside to all of this - now you have to add additional tracking and management for creating and destroying the heaps. But still, it all depends on the use-case.
And that would be about it for today. If you want to learn more about Heap API, I encourage you to check the official docs.
Next time Iâm going to talk a bit more about Memory API and VirtualAlloc, which is what gets called by HeapAlloc. Until then, if you enjoyed this article, Iâd appreciate if you share it with your friends & colleagues :)
Thanks for reading!
Mixa
Other articles from the C++ Memory Management series: