How Can I Explicitly Free Memory in Python?

Usually, as a developer, you have to write a program dealing with lots of documents, processing huge volumes of data and information, and keeping the data in the memory. Therefore, this leads to a shortage of memory space that impacts the overall performance of your code. 

To prevent the memory shortage, you have to clear the memory by clearing the data or information that is no longer required in the program.This tutorial will guide you on how the memory gets allocated and the different methods to clear the memory in Python.

How is Memory Allocated in Python?

In Python, everything is an object. These objects are stored in the memory to be accessed. Before the objects can be stored in memory, a lump of memory initially gets allocated. Python’s memory allocator makes sure that there is available space to store these objects. For this, it interacts with the memory administrator of your operating system.

Python uses dynamic memory allocation. One benefit of this is that it communicates how much memory you need for your program in advance. Another benefit is that data structure manipulation is possible as you can allocate the memory as per your needs if the data structure expands. However, every coin has two sides. Since dynamic memory allocation continues as long as the program is in the state of execution, it consumes more time. Likewise, the memory that has been used should be freed after it has been utilized to overcome this drawback. 

Garbage Collection in Python

In Python, you cannot explicitly free the memory. To clear memory, you have to ensure that you don’t keep storing the references to the objects. This will ensure that the memory gets cleared when they are garbage-collected. Garbage collection is carried out by a program to clear the previous memory for an object that is not being used.

Since reference cycles take computational work, garbage collection should be automatic. In Python, garbage collection is dependent on object allocations and de-allocations. It saves a lot of issues accordingly that come with manually managing the memory allocation and de-allocation. In particular, Python utilizes reference counting along with garbage collection to clear up the unused memory. 

Now, what’s reference counting?🤔

💡Reference Counting

Whenever the developer creates an object in Python, the underlying object will have both- a reference object and its Python type like lists, dictionary, etc. When the object is referenced in the program, its reference count gets incremented, and it gets decremented when an object is dereferenced. In case an object’s reference count becomes 0, the memory for the object is deallocated. 

How to view the reference count?

One can check the reference count of a particular object in Python by importing the sys module. Check the reference count of a variable as shown in the following example:

>>> import sys
>>> x = 'hello'
>>> sys.getrefcount(x)

# 2

Note: The reference count is two as we have first created the variable and then it is passed to the sys.getrefcount() function.

Automatic Garbage Collection Using The gc Module

We can also inspect the threshold for new objects (objects in Python known as generation 0 objects) by loading the gc module and requesting for the garbage collection thresholds:

import gc
print("Garbage collection thresholds: " gc.get_threshold())
Garbage collection thresholds: (500, 10, 10)

The above system has the default threshold of 500. It implies that when the number of allocations is greater by 500 than the number of de-allocations, then the automatic garbage collector will start running.

Example:

import gc
import pprint
import sys
try:
    threshold = int(sys.argv[1])
except (IndexError, ValueError, TypeError):
    print 'Invalid threshold'
    threshold = 5
class MyObj(object):
    def __init__(self, name):
        self.name = name
        print 'Created', self.name
gc.set_debug(gc.DEBUG_STATS)
gc.set_threshold(threshold, 1, 1)
print 'Thresholds:', gc.get_threshold()
print 'Clearing the collector'
gc.collect()
print
print 'Creating objects'
objs = []
for i in range(10):
    objs.append(MyObj(i))

Output:

Thresholds: (5, 1, 1)
Clearing the collector
gc: collecting generation 2...
gc: objects in each generation: 218 2683 0
gc: done, 0.0007s elapsed.
Creating objects
gc: collecting generation 0...
gc: objects in each generation: 7 0 2920
gc: done, 0.0000s elapsed.
Created 0
Created 1
Created 2
Created 3
Created 4
gc: collecting generation 0...
gc: objects in each generation: 6 4 2820
gc: done, 0.0000s elapsed.
Created 5
Created 6
Created 7
Created 8
Created 9
gc: collecting generation 2...
gc: objects in each generation: 5 6 2817
gc: done, 0.0005s elapsed.

Note: Automatic garbage collection won’t run if your device is running out of memory; rather your application will throw exceptions, which should be taken care of, or your application crashes. In this manner, any part of your code that frees up a huge block of memory has a decent possibility for running manual garbage collection.

Manual Garbage Collection Using gc.collect()

The application must be as liberated from reference cycles as possible. Invoking the garbage collector manually during the program execution is the best way to deal with the memory being consumed by reference cycles. To invoke the garbage collector manually you can use the gc.collect(). The gc.collect() mehod is used to return the number of objects it has collected and de-allocated during the program execution. We can invoke the collector using the following way:


import gc
x = gc.collect()

Example:

import gc
import pprint
class Graph(object):
    def __init__(self, name):
        self.name = name
        self.next = None
    def set_next(self, next):
        print('Linking nodes %s.next = %s' % (self, next))
        self.next = next
    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, self.name)
# Construct a graph cycle
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)
print()
# Remove references to the graph nodes in this module's namespace
one = two = three = None
# Show the effect of garbage collection
for i in range(2):
    print('Collecting %d ...' % i)
    n = gc.collect()
    print('Unreachable objects:', n)
    print('Remaining Garbage:', end=' ')
    pprint.pprint(gc.garbage)
    print()

Output:

Linking nodes Graph(one).next = Graph(two)
Linking nodes Graph(two).next = Graph(three)
Linking nodes Graph(three).next = Graph(one)

Collecting 0 ...
Unreachable objects: 6
Remaining Garbage: []

Collecting 1 ...
Unreachable objects: 0
Remaining Garbage: []

In the above program, the cycle is cleared when garbage collection runs the initial time since nothing refers to the Graph nodes aside from themselves. The gc.collect() method returns the number of inaccessible objects found. 

Please stay tuned and subscribe for more solutions and interesting discussions in the future. Happy Learning!


Where to Go From Here?

Enough theory, let’s get some practice!

To become successful in coding, you need to get out there and solve real problems for real people. That’s how you can become a six-figure earner easily. And that’s how you polish the skills you really need in practice. After all, what’s the use of learning theory that nobody ever needs?

Practice projects is how you sharpen your saw in coding!

Do you want to become a code master by focusing on practical code projects that actually earn you money and solve problems for people?

Then become a Python freelance developer! It’s the best way of approaching the task of improving your Python skills—even if you are a complete beginner.

Join my free webinar “How to Build Your High-Income Skill Python” and watch how I grew my coding business online and how you can, too—from the comfort of your own home.

Join the free webinar now!