CodeNebula
Python🚀Advancedadvanced

Memory Management and the `gc` Module

Python · Advanced · Run with python3

Explanation

CPython uses reference counting as its primary GC mechanism — an object is freed as soon as its reference count drops to zero. A cycle detector handles reference cycles (objects pointing to each other). The `gc` module controls the cycle collector: `gc.collect()` forces a collection, `gc.disable()` turns it off, `gc.get_threshold()` returns collection thresholds. `weakref` creates references that don't increase the reference count — useful for caches and observer patterns without preventing garbage collection.

Real-World Scenario

A cache holding 10,000 objects: if the cache holds strong references, the objects never get garbage collected. Using `weakref.WeakValueDictionary` instead means the cache automatically shrinks when objects are no longer used elsewhere — no manual eviction needed. ```python import gc import weakref import sys class Node: def __init__(self, value): self.value = value self.next: "Node | None" = None # Reference cycle — would leak without cycle GC a = Node("A") b = Node("B") a.next = b b.next = a # Cycle! print(f"GC before collect: {gc.get_count()}") # (counts objects in each gen) del a, b collected = gc.collect() print(f"Cycle GC collected {collected} objects") # weakref — cache that doesn't prevent GC class ExpensiveObject: def __init__(self, key): self.key = key cache: weakref.WeakValueDictionary = weakref.WeakValueDictionary() obj = ExpensiveObject("config") cache["config"] = obj print(cache.get("config")) # <ExpensiveObject ...> del obj gc.collect() print(cache.get("config")) # None — auto-evicted ```

Quick Snippet

snippet.py
import gc
import weakref
import sys

class Node:
    def __init__(self, value):
        self.value = value
        self.next: "Node | None" = None

# Reference cycle — would leak without cycle GC
a = Node("A")
b = Node("B")
a.next = b
b.next = a  # Cycle!

print(f"GC before collect: {gc.get_count()}")  # (counts objects in each gen)
del a, b
collected = gc.collect()
print(f"Cycle GC collected {collected} objects")

# weakref — cache that doesn't prevent GC
class ExpensiveObject:
    def __init__(self, key): self.key = key

cache: weakref.WeakValueDictionary = weakref.WeakValueDictionary()
obj = ExpensiveObject("config")
cache["config"] = obj
print(cache.get("config"))  # <ExpensiveObject ...>
del obj
gc.collect()
print(cache.get("config"))  # None — auto-evicted

Full Example

memory_management.py
# Compile: python3 memory_management.py
# Output:
# Cycle GC collected objects
# None (auto-evicted)

import gc
import weakref

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

a = Node("A")
b = Node("B")
a.next = b
b.next = a  # Reference cycle

del a, b
collected = gc.collect()
print(f"Cycle GC collected {collected} objects")

# weakref — cache that doesn't prevent GC
class ExpensiveObject:
    def __init__(self, key): self.key = key
    def __repr__(self): return f"ExpensiveObject({self.key!r})"

cache: weakref.WeakValueDictionary = weakref.WeakValueDictionary()
obj = ExpensiveObject("config")
cache["config"] = obj
print(f"In cache: {cache.get('config')}")

del obj
gc.collect()
print(f"After del: {cache.get('config')}")  # None

# Reference counting basics
import sys
x = [1, 2, 3]
print(f"Ref count for x: {sys.getrefcount(x)}")  # 2 (x + getrefcount arg)
y = x
print(f"Ref count after y=x: {sys.getrefcount(x)}")  # 3
del y
print(f"Ref count after del y: {sys.getrefcount(x)}")  # 2
Run command
python3 memory_management.py