Fix Cache Memory Leaks with weakref
Learn how Python's weakref.WeakValueDictionary prevents memory leaks in caches by automatically cleaning up entries when objects are no longer needed elsewhere.
Python's weakref for Caches Without Leaks
You've built a cache in Python. You're proud of it. But then you notice something odd — your program's memory keeps growing, even when it shouldn't. The culprit? Strong references in your cache that prevent objects from being garbage collected.
Let me show you how weakref saves the day, with real examples you can use today.
The Cache Problem
Imagine you're building a web scraper at PythonSkillset that processes thousands of images. You cache expensive results to avoid recalculating:
class ImageProcessor:
def __init__(self):
self.cache = {} # This holds strong references
def process(self, url):
if url in self.cache:
return self.cache[url]
result = expensive_processing(url)
self.cache[url] = result # Strong reference keeps result alive
return result
Looks innocent, right? But every processed result stays in memory forever, even when nothing else needs it. That's a memory leak.
Enter weakref
weakref creates references that don't prevent garbage collection. The magic happens when an object has only weak references left — Python's garbage collector will clean it up.
import weakref
class ImageProcessor:
def __init__(self):
self.cache = weakref.WeakValueDictionary() # No more leaks!
def process(self, url):
if url in self.cache:
return self.cache[url]
result = expensive_processing(url)
self.cache[url] = result # Object can still be garbage collected
return result
When Objects Disappear
Here's the trick — if nothing else holds a strong reference to your cached object, weakref.WeakValueDictionary automatically removes that entry when the object is collected. No manual cleanup needed.
class ImageData:
def __init__(self, url):
self.url = url
print(f"Loading {url}")
def __del__(self):
print(f"Unloading {self.url}")
cache = weakref.WeakValueDictionary()
img = ImageData("photo.jpg")
cache["photo"] = img
print(cache.get("photo")) # Works fine
del img # Remove strong reference
print(cache.get("photo")) # Returns None — object was cleaned up
Real-World Use at PythonSkillset
At PythonSkillset, we use this pattern for: - Database query caches — When a user session ends, all their cached queries are auto-cleaned - API response caches — Temporary responses that expire when the session object goes away - Worker task results — Results that should disappear when worker processes restart
The WeakValueDictionary Pattern
This is your go-to tool for caches:
import weakref
class TaskCache:
def __init__(self):
self._cache = weakref.WeakValueDictionary()
def get_or_compute(self, task_id, compute_func):
result = self._cache.get(task_id)
if result is None:
result = compute_func()
self._cache[task_id] = result
return result
What About Simple Caches?
For simple value caches (strings, numbers, tuples), weakref won't help much — those are immutable and get copied anyway. But for large objects like database records, image data, or API responses, it's a lifesaver.
A Warning
Don't use WeakValueDictionary when you need guaranteed cache persistence. If your application holds no other references to cached objects, they'll vanish. That's the feature, not a bug — but it can surprise you.
The Bottom Line
Memory leaks in Python caches are a real problem, but weakref. WeakValueDictionary gives you automatic memory management without complex cleanup code. Your cache stays lean, your objects get collected when they should, and you stop chasing phantom memory growth.
Try it next time you build a cache that holds large objects. Your future self will thank you when the memory graph stays flat during long Python sessions.
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.