What’s all the hype about the heap and Stack?

The terms Stack and Heap get thrown around a lot in the programming world. But what does it means? What is the difference between them and why should we care?

Introduction

To start, I would like to make a clarification on a possible misconception. Heap is referred here as a memory allocation technique, not as a data structure. Furthermore, when we speak about Heap as opposed to Stack, we do not mean two types of memory, what we mean is two ways of allocating memory.

When your code compiles (or run if interpreted), the compiler needs to consider how to allocate your variable definitions in memory. To understand this process, let’s first examine the pros and cons of each type of allocation.

Heap
ProsCons
Big Memory SizeSlow to allocate and access
Flexible resizingManaged by programmer

Stack
ProsCons
Fast to allocate and accessLimited fixed size of memory
Managed by compiler/interpreter

What does it all means?

Let’s take a look at how memory is managed through a Stack. Consider the following code snippet.

package main

import "fmt"

func main() {
	val1 := 3
	val2 := 4
	fmt.Println(sum(val1, val2))
}

func sum(a, b int) int {
	result := a + b
	return result
}

Every thread gets assigned by the operating system a section of memory to be used as a Stack. A Stack, if you remember, is a data structure that guarantees a FILO (First In Last Out) interface. Which means that the first element pushed into the Stack is the last element to get retrieved from it.

Stack in action

Let’s follow the previous animation. When we call the main function the compiler allocates space in a Stack as it encounters variable declarations. When sum(a, b int) int is called, the compiler first allocates memory for the return value, move the pointer of the return value to that location, and then designates the rest of the parameters. Since there are no local variables in this function, the compiler does not allocate any more space for that.

After the code is executed, the result is placed in the spot allocated for the return value (Notice how the compiler does not need a name for the return value since it has adjusted the pointer to that location.)

At the moment of return, the compiler will assign the value to the result variable. Finally, the output “7” is displayed and main deallocates all the variables in the Stack.

The purpose of the Stack is to maintain the scope. Since we move the pointer of the return upon each function call, we can maintain a local reference to all the variables without worrying about the variables in the previous function in the Stack. As finish execution of functions, we keep deallocating space from the Stack, thus making it available for use on the next call.

Looking at this you might realize now why we have Stack overflows. If you keep calling a function recursively, the Stack gets crowded with new allocations. Since it is of fixed size, it eventually overflows.

What is the Heap then?

The Heap is in another space in memory. The Heap is reserved for variables and objects that are not encapsulated within the scope of functions. Or by values that “escape” function execution. Before we get to the “escape” part, let us see an example of when the Heap is used.

package main

import "fmt"

var globalV = "Hello World!"

type Person struct {
	name string
	age int
}

func main() {
	person := &Person{
		name: "Steve",
		age: 25,
	}
	fmt.Println(person)
	person2 := changeAge(person)

	fmt.Println(person)
	fmt.Println(person2)

}

func changeAge(p *Person) *Person{
	elizabeth := &Person{
		name: "Elizabeth",
		age: 26,
	}
	p.age = 45
	return elizabeth
}

Let’s analyze this code. The Person struct is created and instantiated in the main function.

Heap and Stack relationship

As you can see, these structs are generated in the Heap. However, the reference is stored in the Stack. These references (the variables that hold the addresses) are local to the functions scopes, but the actual values of the structs are globally shared. If you notice in the animation. The age of “Steve” is changed within the function changeAge and the value of “Elizabeth” is passed around all the way to &person2. But it is the address what is copied, the structure remains in the Heap and is shared between the functions.

Conclusion

The Heap and the Stack are two ways of managing the memory within a program. The Stack maintains the scope of the functions while the Heap holds the globally shared values. Since the Stack is handled by the compiler, you as a programmer do not need to worry about it unless you flood it with information. The Heap; on the other hand, needs to be managed by you. That means you need to allocate and deallocate the space and it could lead to memory leaks. But you have much more flexibility on how the memory is managed so it could definitely lead to a performance optimization (By sharing a large structure around or maintaining it as a singleton.)

Fortunately, most modern programming languages provide garbage collection to remove the unused referenced to the Heap. But garbage collection is a topic on itself that should be left for another day.

Leave a Reply

Your email address will not be published. Required fields are marked *