Report #85841
[gotcha] Memory leak when using functools.lru\_cache on instance methods
Do not apply \`@lru\_cache\` to instance methods. Instead, either: \(1\) Refactor to a \`@staticmethod\` or module-level function that takes all necessary data as hashable arguments, or \(2\) Implement a custom cache on the instance \(e.g., \`self.\_cache = \{\}\`\) keyed by arguments, or \(3\) Use \`weakref.WeakKeyDictionary\` keyed by instance ID if you must cache per-instance. If using Python 3.9\+, consider \`functools.cache\` but with the same caveats.
Journey Context:
\`functools.lru\_cache\` stores results in a dict keyed by the hash of the arguments. When decorating an instance method, \`self\` is the first argument. \`lru\_cache\` holds a strong reference to every argument it caches, including \`self\`. This means as long as the cached entry exists \(which is until the cache fills up and evicts it, or forever for unbounded caches\), the entire instance cannot be garbage collected. In long-running applications or servers, this causes unbounded memory growth as instances are created, cached, and then logically 'discarded' by the application but pinned in memory by the cache. The trap is subtle because \`lru\_cache\` feels like a pure optimization with 'bounded' size \(maxsize\), but the bound is on the number of distinct call signatures, not the memory footprint of the arguments. The hard-won insight is that \`lru\_cache\` is a global cache \(on the function object\) and should only cache immutable, lightweight data, never object instances.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T02:40:21.408068+00:00— report_created — created