How to Use a Weakref Cache to Avoid Memory Leaks in Python

This code demonstrates building a value cache with weakref.WeakValueDictionary so objects can be garbage collected when no longer referenced, preventing memory leaks.

Medium Python 3.9+ Aug 9, 2026 Concurrency & performance 13 views 0 copies

Python code

43 lines
Python 3.9+
import weakref
import gc


class ExpensiveObject:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return f"ExpensiveObject('{self.name}')"


class ObjectCache:
    def __init__(self):
        self._cache = weakref.WeakValueDictionary()

    def get_or_create(self, name):
        obj = self._cache.get(name)
        if obj is None:
            obj = ExpensiveObject(name)
            self._cache[name] = obj
        return obj

    def __len__(self):
        return len(self._cache)


if __name__ == "__main__":
    cache = ObjectCache()

    obj1 = cache.get_or_create("alpha")
    obj2 = cache.get_or_create("alpha")

    print(f"Same object: {obj1 is obj2}")
    print(f"Cache size with reference: {len(cache)}")

    # Drop the only strong reference
    del obj1
    del obj2
    gc.collect()

    print(f"Cache size after GC: {len(cache)}")
    print("Memory leak avoided via weakref cache")

Output

stdout
Same object: True
Cache size with reference: 1
Cache size after GC: 0
Memory leak avoided via weakref cache

How it works

A WeakValueDictionary stores weak references to values, so when all strong references to an object are dropped, the object can be garbage collected and automatically removed from the cache. In get_or_create, we check the cache first and only construct a new ExpensiveObject if the key is missing, ensuring reuse while the object is alive. The gc.collect() call forces garbage collection to demonstrate that the cache does not keep objects alive. This pattern gives you the performance benefit of caching without the memory leak that a regular dictionary would cause.

Common mistakes

  • Using a regular dictionary instead of WeakValueDictionary, which pins objects in memory.
  • Forgetting that WeakValueDictionary only supports weakref-able value types; use WeakKeyDictionary for key-based caching.
  • Expecting the cache to retain values that have no other strong references.
  • Not calling `gc.collect()` in tests to see the effect if the interpreter doesn't collect immediately.

Variations

  1. Use `functools.lru_cache` for memoization with size limits instead of manual caching.
  2. Use a `WeakKeyDictionary` when you want weak keys and strong values.

Real-world use cases

  • Caching database query results so that removing references frees memory automatically.
  • Storing loaded configuration or connection objects that should vanish when their owners are done.
  • Implementing a resource pool for expensive objects like model instances without leaking memory.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Concurrency & performance

Related tutorials and quizzes for this topic.