Memory Management in Python

Memory Management In Python

Memory Management is the process of storing memory dynamically and freeing it when not in use.


Understanding Memory Management in Python

In Python, this allocation and deallocation of memory are done automatically by the Python garbage collector created by python developers so that users don’t have to do manual garbage collection.

Garbage Collection

Garbage collection in Python is the memory management process of freeing up the unused and unwanted memory for our programs by the interpreter. In Python, this is done automatically.

The garbage collector finds the object that has no reference pointing to it and then deletes that object from the heap memory. For this, Python uses a reference counting algorithm.

Example:

class Python:

    def __init__(self):
        print('The object is created.')

    def __del__(self):
        print('The object is destroyed.')

obj1 = Python()
obj2 = obj1
obj3 = obj1
print("Set obj1 to None")
obj1 = None
print("Set obj2 to None")
obj2 = None
print("Set obj3 to None")
obj3 = None

Output:

The object is created.
Set obj1 to None
Set obj2 to None
Set obj3 to None
The object is destroyed.

Here, we created an object of class Python and passed its reference to obj1, obj2, obj3. This makes the reference count of that object 3. Then, when we assign these references to none, all the references from that object are removed and become 0. As there are no references to the object , it is destroyed by python garbage collector and __del__() method is executed.

Reference Counting

Reference counting in Python is a technique in which an object is deallocated from the memory when there are no references pointing to it. When the reference count becomes zero, the object is deleted.

We have an inbuilt function getrefcount() present in python module sys which returns the number of references for the given python object.

Example:

import sys
str = "Welcome to Python"
print(sys.getrefcount(str))

arr = []
arr.append(str) # appending to an array
print(sys.getrefcount(str))

dict = {}
dict['str'] = str # adding to a dictionary
print(sys.getrefcount(str))

arr = [] # resetting the array
sys.getrefcount(str)
dict['my_str'] = "Some other string"
print(sys.getrefcount(str))
 

Output:

4
5
6
5

The value of reference count is one high than what you expect because it also counts reference count for the object passed in the function sys.getrefcount().

Sometimes the reference count of an object never reaches zero. This happens because the object refers to itself. This is called a Reference cycle.

Example:

import sys
x = []
x.append(x) # x contains reference to itself
print("Reference count of object is",sys.getrefcount(x))

Output:

Reference count of object is 3

Here, an object x is created which refers to itself. The reference count will never reach 0 as it has its own reference. The object x will occupy the memory until the Python garbage collector is invoked.

When object is declared globally, the reference count of an object can never become zero.

Memory Allocation

To understand memory allocation, we must understand Random Access Memory(RAM). RAM is also referred to as the main memory that allows information to be stored and retrieved on a computer.

At the top of RAM, we have a stack and at the bottom, we have heap. Heap is responsible for storing variables/values and stack is responsible for holding the references to the object in the heap.

Stack
Stack and Heap locations in RAM

In Python, when more than one variables have the same value, a second variable that points to the original value is created in the heap.

Example:

x = 5
y = x 
if(id(x) == id(y)):
   print("x and y refer to the same object")

x = x+1
if(id(x) != id(y)):
    print("x and y refer to different objects")

z = 5
if(id(y) == id(y)):
   print("y and z refer to same memory")

Output:

x and y refer to the same object
x and y refer to different objects
y and z refer to same memory

There are two types of memory allocations:

  • Stack Memory Allocation
  • Heap Memory Allocation.

1. Stack Memory Allocation

Stack memory allocation is the storage of static memory inside a particular function or method call. When the function is called, the memory is stored in the function call stack. Any local variable initializations are stored in call stack and deleted once the function returns.

So, when we run our program, all the functions are first stored in call stack and then deleted when the function is returned.

Example:

def func():
    #These initializations are stored in stack memory
    x = 10 
    y = "Apple"
     

2. Heap Memory Allocation

Heap memory allocation is the storage of memory that is needed outside a particular function or a method call. This memory is used in the program at global scope.

Heap memory is not related to heap data structure. It is simply a large space of memory provided to users when they want to allocate and deallocate variables/values.

In Python, heap memory is managed by interpreter itself and the user has no control over it.

Example:

def func()
    #Allocates memory for 5 integers in heap memory
    x=[None]*5

Conclusion

Programmers fell in love with Python for its superior memory management capabilities. Compared to many other low-level programming languages, Python made it a breeze to work with variables without worrying about overusing resources.


References

Official Docs