Stack vs Heap Memory - What are the differences?

Stack vs Heap Memory - What are the differences?

by | 8 min read
Published:
Updated:

In modern programming languages such as C# or Java, we tend to take memory management for granted.

Gone are the days when we need to call malloc to request enough memory for our variables. Luckily a lot of that is done for us by the runtimes so we don’t usually need to allocate and deallocate memory.

Understanding how the underlying memory management works as well as where our variables are stored can still be really helpful when it comes to understanding the scope of our variables.

If you are programming in C or C++ then understanding heap memory and stack memory is going to be essential knowledge.

In this article, we are going to look at what stack and heap memory are, their key differences, and where your variables are stored when they are in memory.

Memory Layout

At a high level, the memory for your application is laid out like this:

Stack vs Heap

This is slightly simplified as there are other areas of memory but this is enough to cover what we are looking at today.

🚀 Are you looking to level up your engineering career?

You might like my free weekly newsletter, The Curious Engineer, where I give career advice and tackle complex engineering topics.

📨 Don't miss out on this week's issue

At runtime, your compiled code is stored in memory as execution instructions in the bottom part of memory (machine code).

How do heap and stack grow?

The stack and heap both share the same address space. As shown in the diagram the heap grows upwards and more space is allocated to it as you need it, either manually by the programmer (C/C++) or automatically by the runtime.

The stack is located in the high address space. Items are added to the stack moving downwards going from high address space to low address space. However, the size of the stack is generally fixed when the application is compiled. This is why if you run a recursive function in an infinite loop you will get a stack overflow exception.

Even though the stack and heap take up space towards each other the operating system will make sure that they don’t consume the same address space.

What is the stack?

The stack has 2 main responsibilities when your program is running:

  1. To keep track of the method that control should return to once execution has finished for the current method.

  2. To hold the values (or pointers) of local variables used in the methods.

How does the call stack work?

Each time you call a method in your application it is added (pushed) to the call stack along with any local variables that are declared in the call stack.

How does the call stack work

Once the execution of that method has finished it is removed (popped) from the call stack and execution is returned to the previous method.

Stack Data Structure

To properly understand the call stack you need to understand the stack data structure.

The stack can be viewed similarly to a stack of books. You can add and remove books from the top of the stack but you can’t access any in the middle or the bottom.

The stack works on the Last In First Out (LIFO) principle. You can only ever read the data from the item on the top of the stack.

This is why when you call a new method you don’t have access to any of the variables that were used in the calling method. All of those variables are sitting in the previous block and are therefore inaccessible.

Stack vs Array

The stack isn’t like an array. In an array, you can access any item in the array using its index. With a stack, you only have access to the last item you added to it.

Stack vs Queue

A queue works on the First In First Out (FIFO) principle so it has some similarities to a stack. Like a queue, you can’t access items in the middle of the stack only those at the end of the queue.

A stack is basically a one-ended queue.

What is the heap?

The heap is a section of memory that allows dynamic allocation of memory and is not bound by the same rules as the stack.

This means that if you want to allocate memory to store a large amount of data then the heap is the best place to do it.

Stack vs Heap

If you need access to data throughout your application then this data will also be stored on the heap.

Heap Management

If you are using a programming language such as Java or C# then memory on the heap will be allocated and deallocated for you. Both of these runtimes have “Garbage Collectors” (GC) which go through the process of cleaning up unused blocks of heap memory.

Allocating and deallocating memory on the heap has a performance impact as to is not as quick as adding and removing items from the stack.

Running profiling on your application will let you see how often the garbage collector is removing items from the heap. You can often get some good performance improvements if you can optimise how often memory is allocated.

Differences in Stack and Heap Memory

We have mentioned already some of the key differences between heap memory and stack memory.

The table below shows all the other differences in heap and stack memory:

FeatureStackHeap
Access SpeedFastSlow
Memory AllocationHandled automatically by runtimeOnly automatically handled in high level languages
Performance CostLessMore
SizeFixed SizeDynamic Size
Variable AccessLocal variables onlyGlobal variable access
Data StructureLinear data structure (stack)Hierarchical Data Structure (array/tree)
Main IssueSmall fixed amount of memory (stack overflow risk)Memory fragmentation over time

Where are variables stored?

Variables are stored on either the stack or heap depending on where they are declared and what type of variables they are.

Value Type Variables

Value-type variables declared in a method are always stored on the call stack along with the method. These are the data types that you typically have for value types.

bool, byte, char, decimal, double, enum, float, int, long, sbyte, short, struct, uint, ulong, short

The exception to this is global variables or variables that are declared in a class will always be on the heap. Global variables need to be accessible across multiple function calls and therefore cannot live on the stack.

Reference-Type Variables

Reference type variables always live on the heap. Reference types are made up of 2 parts:

  1. Pointer to an address in memory.

  2. The actual data value.

When you declare a reference type variable in a method the pointer stays on the call stack with the method and the value is on the heap.

Reference Types on the Heap

Once the method has finished executing it is popped off the stack and therefore the value on the heap no longer has anything pointing to it. This is where the garbage collector comes in to clear up the memory (assuming you are using something like Java or C#).

Exceptions to where variables are storage

If only everything was as clear-cut as value types on the stack and reference types on the heap.

Where do static variables live?

The fact that they are static means they are not dynamically allocated. Static variables are allocated memory at the start of execution and memory isn’t freed until the application has finished execution.

In C# static variables are stored on a special part of the heap called the High-Frequency Heap. This is also where internal data structures are stored such as the method tables which have pointers to the method implementations.

In C and C++ (and probably a few other languages), static variables are stored in the data segment (the initialized/uninitialised data blocks in the diagram at the start of this post).

If you look up diagrams for where the High-Frequency Heap is stored, you will see that it is basically in the same place as the Data Segment and they seem to do very similar things. It is just a slightly different implementation between the different runtimes.

There is an old Microsoft article that I had to dig out the archives that explains it in-depth.

There is also a good simpler article by Jon Skeet on the topic.

Variable access in anonymous functions

C# has anonymous functions which can be created and called from inside another function. The key thing with anonymous functions is that they have access to the local variables from the method that was created and called it.

Anonymous Functions

When the anonymous function is called it is added to the call stack like any other method. Therefore the only way that it can have access to the variables from the calling method is if they are added to the heap.

Result storage from asynchronous methods

When we call asynchronous methods they run on a different thread. Each new thread gets its own call stack which can execute separately from the main thread.

Asynchronous methods

Asynchronous methods can finish executing before the main thread and therefore the return values need to be stored on the heap so the main thread can access them.

When to use Stack or Heap?

In most high-level programming languages the decision of whether to use stack or heap will be made up for you.

However, if you do need to decide which to use then these are the main rules.

  • Use the stack when your variable is small and is not going to be used after a method has finished executing.
  • Use the heap if the variable is large or needs to be accessed beyond the lifetime of a method.

🙏 Was this helpful? If you want to say thanks, I love coffee ☕️ , any support is appreciated.


ALSO ON ALEXHYETT.COM

SOLID Principles: Do You Really Understand Them?

SOLID Principles: Do You Really Understand Them?

  • 16 June 2023
SOLID Principles are one of those things that every developer has heard of but few fully understand. If you use an object-oriented language…
Recursion explained with the help from Inception

Recursion explained with the help from Inception

  • 12 May 2023
People love to joke about recursion. As the saying goes: “In order to understand recursion, one must first understand recursion.” You will…
Python List Comprehension: With Examples

Python List Comprehension: With Examples

  • 20 February 2023
Working with arrays can be a bit of a pain in most languages. If you want to make changes to an array you need to use a FOR loop, a bit like…
Finally Understand Regular Expressions: Regex isn't as hard as it looks

Finally Understand Regular Expressions: Regex isn't as hard as it looks

  • 10 January 2023
There’s nothing like a regular expression to strike fear in the heart of a developer. Regular expressions (regex) are used for a lot of…
How I would learn to code (if I could start over)

How I would learn to code (if I could start over)

  • 06 January 2023
When I was 8 years old I learnt how to code. I learnt to code from an old BASIC book that my Dad had lying around from his ZX Spectrum. I…
Understanding Big-O Notation

Understanding Big-O Notation

  • 03 January 2023
It’s important when you’re writing applications especially, those that are going to be processing a large amount of data that you understand…
Git Flow vs GitHub Flow

Git Flow vs GitHub Flow

  • 10 November 2022
Losing code that you have spent hours writing can be painful, which is why we use version control (or source control) to store our code and…
Bitwise Operators and WHY we use them

Bitwise Operators and WHY we use them

  • 26 October 2022
Bitwise operators are one of those concepts that a lot of programmers don’t understand. These are not used a great deal anymore so you can…