Stop Memory Leaks in Python with weakref
Learn how Python's weakref module eliminates memory leaks caused by circular references. This guide covers real caching scenarios, the callback trick, and when to use weakref for cleaner memory management.
Memory leaks in Python are sneaky. You write code, it works fine, but over time your application starts eating up more and more RAM until it crashes. I've seen this happen at PythonSkillset.com with long-running services, and the culprit is often circular references that the garbage collector can't handle.
Here's the thing most Python developers don't realize – Python's reference counting normally cleans up unused objects immediately. But when objects reference each other in a loop, those reference counts never drop to zero. The garbage collector eventually catches them, but it runs periodically, not instantly. In a tight loop, this can cause memory to balloon.
Where memory leaks actually happen
Let me show you a real scenario. Say you're building a caching system for your web application:
class PageCache:
def __init__(self):
self.cache = {}
def add_page(self, page):
self.cache[page.url] = page
page.cache = self # Circular reference!
That page.cache = self creates a circular reference. The PageCache holds a reference to the page, and the page holds a reference back to the cache. Neither can be freed.
How weakref solves this
The weakref module lets you hold a reference to an object without increasing its reference count. When the object is destroyed, the weak reference just returns None.
Here's the same cache using weakref:
import weakref
class PageCache:
def __init__(self):
self.cache = {}
def add_page(self, page):
self.cache[page.url] = weakref.ref(page)
def get_page(self, url):
ref = self.cache.get(url)
if ref:
page = ref()
if page is not None:
return page
else:
# Page was garbage collected
del self.cache[url]
return None
Now the cache doesn't prevent the page from being cleaned up. The cache holds a weak reference, and when the page is no longer needed elsewhere, it gets freed properly.
When to actually use weakref
You don't need weakref for everything. Here's where it makes sense:
- Caches – You want cached items to be freed when memory is needed
- Observer patterns – The subject shouldn't prevent observers from being garbage collected
- Large data structures – Trees or graphs where child nodes reference parents
- Memory-sensitive applications – Long-running services, mobile apps, embedded systems
The callback trick
weakref has another superpower – you can attach a callback when the object is about to be destroyed:
def cache_cleanup(url):
print(f"Cleaning up cache entry for {url}")
class Page:
pass
page = Page()
ref = weakref.ref(page, lambda r: cache_cleanup("example.com/page1"))
This fires automatically when the page is garbage collected. Perfect for cache invalidation.
What about normal references?
Most of the time, Python handles memory fine. Use weakref only when you have circular references or want optional references that don't keep objects alive. Overusing weakref can make code harder to debug since objects disappear unexpectedly.
The final word
Memory leaks in Python usually aren't from bugs – they're from design patterns that create unintended object retention. weakref gives you fine-grained control over this. Use it for caches, callbacks, and any situation where you want to say "I'd like to use this object, but I don't want to keep it alive."
At PythonSkillset.com, we've cut memory usage in half on some services just by switching cache references to weakref. It's not magic – it's just understanding how Python manages memory, and using the right tool for the job.
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.