STEM

Stacks and Heap Basics in Python

Python, like most high-level programming languages, has the concept of stack and heap, although it might not be as explicit or direct as in C++ or Java. While we may not need to typically explicitly manage memory in Python, having this knowledge equips us with the skills to write better code, debug issues effectively, and understand the inner workings of our programs. Therefore, understanding stack and heap concepts still remain important for several reasons I’ll discuss below. I’ll also provide overview of stacks and heaps along with code to take a peek inside each of them.

Stacks and Heaps

  • Stack: This is where function calls and local variables are stored.
    Whenever a function is called, a new stack frame is created, containing that function’s local variables and parameters. Once the function returns, the stack frame is destroyed.
    This means that the stack is used for managing the execution context of functions and is typically very fast but limited in size.
  • Heap: This is where objects and data that need to persist beyond the lifetime of a single function call are stored.
    When you create objects in Python (like lists, dictionaries, or instances of classes), they are stored in the heap. The heap is managed by Python’s garbage collector, can grow as needed, and the garbage collector automatically reclaims memory that is no longer in use.

How Python handles Stacks and Heaps

The beauty of Python lies in its simplicity and abstraction. We can create variables, objects, and functions without needing to know where they are stored
in memory. Python’s garbage collector automatically reclaims memory that is no longer in use, which helps prevent memory leaks and other issues.

As a Python developer, we generally don’t need to worry about the details of stack and heap memory management. Python handles this for us with its built-in memory management system and garbage collector. This allows us to focus more on writing our code rather than managing
memory allocation and deallocation.

Take a Peek Inside the Stack and Heap

The code below creates variables and objects in the stack and heap and displays what’s inside them at specific stages of the execution. To run it, click on the widget to run it and see the contents of stack and heap on the right pane. The code is shown in full on the left pane.

A sample output of this looks as follows:

Stack in function1:
{'a': 10, 'x': 2, 'b': <main.MyObject object at 0x7fd79fc10970>, 'frame': }

Object in Heap (created in function1):
Obj: <main.MyObject object at 0x7fd79fc10970>, Name: Test, Value: 100, Data: [1, 2, 3]

In the Heap:
Obj: <main.MyObject object at 0x7fd79fc10970>, Name: Test, Value: 100, Data: [1, 2, 3]

It’s easy to see that the functions function1 and function2 are in the stack along with the variables defined in them. The variables a, x, and b exist in the function1’s stack along with their values. It also creates an object of type MyObject and stores it in b whose value shows the object name and its memory address. The object created from function1 gets placed in that memory in the heap.

We call function2 from function1 to do the object creation, which creates the object by passing its (function1’s) local variables’ values from its stack to create the object and set its values such that it gets its attributes name=Test, value=a raised to the power of b (so, 10^10=100), data=[1,2,3] as a list. These all exist in the heap after the object is created.

After function1 creates the object and execution returns to function2, we take a look at the heap once again, and expectedly, find the object MyObject along with its attributes.

You may notice that although the addresses in memory will change at each run, the address of MyObject shown in function1() as part of stack info, matches exactly with the address of MyOjbect printed in function2() from the heap every time. Also note that the content in the heap when printed from function1() is the same as the one printed in function2() after calling function1()…because, the heap contents persist during the execution of the program.

In Conclusion

It’s important to note that although Python’s garbage collector typically handles memory management well, certain situations cannot be handled by the garbage collector. These include circular references, infinite loops, memory exhaustion (excessive recursions or ever-growing data in memory), or poorly managed resources.

While we may not need to manually manage memory in Python, having this knowledge equips us with the skills to write better code, debug issues effectively, and understand the inner workings of our programs.
Therefore, understanding stack and heap concepts still remain important for several reasons including:

  1. Performance Optimization:
    For example, understanding the difference between stack and heap can help coders make decisions about data structures and their impact on performance.
  2. Debugging and Profiling:
    When something goes wrong, knowing about the stack and heap can help us diagnose issues. For example, stack overflow errors happen when there is too much recursion, and memory leaks occur when objects are not properly cleaned up.
  3. Resource Management (or, avoiding resource leaks):
    Even though Python handles garbage collection, there are times when you need to manually manage resources (e.g., file handles, network connections).
  4. Learning Other Languages:
    Having a good grasp of memory management concepts makes it easier to learn and work with other programming languages that may require explicit memory management, like C or C++.

I hope you found this post helpful and interesting. Explore this site for more tips and articles. Be sure to also check out my Patreon site where you can find free downloads and optional fee-based code and documentation. Thanks for visiting!

Back To Top