Handling Circular References and Memory Leaks in Python with weakref and gc Modules
This article explains how circular references cause memory leaks in Python, describes the role of reference counting and the cyclic garbage collector, and provides practical examples using the weakref and gc modules to create weak references, detect, and manually manage cyclic garbage.
In Python, circular references occur when two or more objects reference each other, forming a loop that prevents the reference‑counting garbage collector from reclaiming the memory, which can lead to memory leaks.
Python mitigates this with a cyclic garbage collector and supports weak references, which do not increase an object's reference count and therefore allow the object to be collected even when other references exist.
1. Create a weak reference using the weakref module:
import weakref
class MyClass:
pass
obj = MyClass()
ref = weakref.ref(obj) # create weak reference
print(ref()) # -> <__main__.MyClass object at 0x...>
# delete the strong reference
del obj
print(ref()) # -> None (object has been collected)2. Use a weak‑reference dictionary:
import weakref
cache = weakref.WeakValueDictionary()
class MyClass:
def __init__(self, key):
self.key = key
obj1 = MyClass("obj1")
obj2 = MyClass("obj2")
cache[obj1.key] = obj1
cache[obj2.key] = obj2
print(cache.get("obj1")) # -> <__main__.MyClass object at 0x...>
# delete a strong reference
del obj1
print(cache.get("obj1")) # -> None3. Manually trigger garbage collection with the gc module:
import gc
class MyClass:
def __init__(self):
self.other = None
obj1 = MyClass()
obj2 = MyClass()
obj1.other = obj2
obj2.other = obj1
# force collection
gc.collect()
print(gc.get_referents(obj1)) # -> []
print(gc.get_referents(obj2)) # -> []4. Find cyclic references using gc :
import gc
class MyClass:
pass
obj1 = MyClass()
obj2 = MyClass()
obj1.other = obj2
obj2.other = obj1
gc.collect()
cycle = gc.garbage
for obj in cycle:
print(obj)5. Disable automatic garbage collection:
import gc
class MyClass:
pass
obj1 = MyClass()
obj2 = MyClass()
obj1.other = obj2
obj2.other = obj1
gc.disable()
# remove strong references
del obj1
del obj2
gc.collect()
print(gc.get_referents(obj1)) # -> []
print(gc.get_referents(obj2)) # -> []6. Set debugging flags for gc :
import gc
gc.set_debug(gc.DEBUG_LEAK)
# run code that may create cycles
gc.collect()
print(gc.garbage)7. Manually clear references:
import gc
class MyClass:
pass
obj1 = MyClass()
obj2 = MyClass()
obj1.other = obj2
obj2.other = obj1
gc.collect()
gc.collect()
print(gc.get_referents(obj1)) # -> []
print(gc.get_referents(obj2)) # -> []8. Adjust garbage‑collection thresholds:
import gc
gc.set_threshold(5)
# run code that may create cycles
gc.collect()9. Perform generational collection:
import gc
class MyClass:
pass
obj1 = MyClass()
obj2 = MyClass()
obj1.other = obj2
obj2.other = obj1
gc.collect(2) # collect up to generation 2
print(gc.get_referents(obj1)) # -> []
print(gc.get_referents(obj2)) # -> []10. Change the collection threshold and run collection:
import gc
class MyClass:
pass
obj1 = MyClass()
obj2 = MyClass()
obj1.other = obj2
obj2.other = obj1
gc.set_threshold(10)
# run code that may create cycles
gc.collect()
print(gc.get_referents(obj1)) # -> []
print(gc.get_referents(obj2)) # -> []These examples demonstrate how to use weak references and Python's garbage‑collection facilities to detect and break circular references, preventing memory leaks and ensuring objects are properly reclaimed.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
