In this article I’ll do my best to explain as simply as possible how memory management in microcontrollers works, what are „stack” and „heap” areas, and what’s the difference between them. Let’s start with a general diagram presenting how „uC” RAM memory is divided into diffrent arreas:
Starting from the bottom, which is the beginning of the memory, there is a segment for the code – at least in case you want to put some part of your code into RAM memory. Most often you don’t need that, so the memory will start with the next segment, which is data. This fragment for „static allocation” so it means that it stores all the global and static variables. What is special about it is that its size was known at the compile time. Next we start with the „heap” area which is dynamically allocated by the programmer. It starts after the „data segment”, and keeps growing. Starting from the end of memory, there is a „stack”. The memory area is allocated automatically by the processor. It keeps growing down, in the opposite direction to the heap. As you can see, it is very important that there will be a free space between them, because both segments change dynamically at „runtime” and it is better when they never meet.
Before I start explaining how „stack” and „heap” areas work, let’s talk a little bit more about memory management. If you are looking for information about those terms, you probably already asked yourself this question: how memory management in microcontrollers works? From the previous chapter you already know that memory can be allocated statically, dynamically and automatically. Each of them refers to different areas of RAM memory.
- The Part allocated statically is usually at the beginning of memory, and you can find there all the global and static variables (that’s why they hold the data throughout the entire program cycle.
- The part allocated automatically starts from a different way and from memory. It begins at last memory cell address (the highest address) and it keeps growing down towards the beginning (the opposite direction to static or dynamically allocated memory).This part includes local variables created inside function or block of program, the address of next cell after the function call (to remember where program counter should go back after the function execution is done) and copy of cpu registers.
- The part allocated dynamically is in between the two previous sections. It starts after the area is allocated statically and should end before the automatically allocated section, with some buffer of free space before them. Because both grow in the opposite direction, it’s a risk that one will overwrite another, so this buffer should be big enough to prevent this situation.
So far we have covered a very important topic about memory management in microcontrollers, and understand now how it works, but what does the term „stack” actually means? Quoting definition from the Wikipedia:
To put it simply, stack is a data stored in the section that we describe as automatically allocated by a microcontroller by doing some „push” operation when adding new data to the stack and a „pop” operation for deleting data from the stack. We can imagine it as a stack of books. Every time we like to add a new book to the stack, we can only do it by adding it to the top ( „push” operation), and the stack keeps growing. We can’t remove the first book that is at the bottom, or even books in the middle of the stack. In order to remove some books, we need to start from the top of the stack, and remove them one by one. This is a LIFO buffer in nutshell, which is an acronym from „Last In First Out”
Every time a program goes into some function which calls another function and this function calls some other function etc., there are new „books” added to the stack. These „books” can be different types of data, as was mentioned before. For example, the return address from the function, registers copy of state before entering the function or local data created inside the executed function. Every time a function calls, another function stack grows because new data is added. To take some data from the stack, the whole function must be executed. Then, for example, local variables used by this function are not needed anymore so they can be removed from the stack. Keep this in mind when writing your own code: too much recurrence or uncontrolled recurrence might cause stack overflow. What is stack overflow ? Stack overflow is a situation where a stack grows too big so it runs out of free memory and starts overwriting dynamically allocated sections. If you want to find out more about stack memory management, watch Abelardo Padro video about it on YouTube. I highly recomended it.
There is one term left to explain in this article: „heap”. As it turns out, „stack” was the term for the data stored in an automatically allocated section. The same applies to the heap, but the difference is that this is a section dynamically allocated by a programmer. Dynamically, it means that during the runtime, but where and when depends on how the program was written. Every time a programmer uses functions like „malloc”, „alloc”, „calloc”, part of this section is used. A very important thing here to mention is that this memory won’t be freed automatically by the CPU when not needed. This is a programmer’s responsibility. Don’t forget about it, because this may cause very serious and hard to debug problems.
To sum up this topic, I would like to show you some graphics which show everything that was covered in this article in a very clear and compact way. Now I hope you won’t mistake a stack with a heap anymore. I’m also sure that knowledge learned today helps you avoid very serious problems caused by bad memory management.