DEV Community

Deep Fix
Deep Fix

Posted on

Fix Python Memory Leaks in Production: Proven Strategies & Tools

Introduction

Memory leaks in long‑running Python services can silently degrade performance, increase latency, and eventually cause crashes. This guide walks you through diagnosing, fixing, and preventing memory leaks in production environments.


1. Reproduce the Leak Locally

  1. Create a minimal reproducer – isolate the suspicious module or function.
  2. Run under a memory monitor (e.g., psutil, top, or Docker stats) to confirm a steady rise in RSS.
  3. Automate the load using a script that mimics production traffic.
import time, random
from myapp import process_item

while True:
    data = [random.randint(0, 1000) for _ in range(10_000)]
    process_item(data)
    time.sleep(0.1)
Enter fullscreen mode Exit fullscreen mode

2. Identify the Culprit

2.1 Use tracemalloc

import tracemalloc
tracemalloc.start()
# ... run workload ...
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
    print(stat)
Enter fullscreen mode Exit fullscreen mode

2.2 Visualise with objgraph

pip install objgraph
Enter fullscreen mode Exit fullscreen mode
import objgraph
objgraph.show_most_common_types(limit=10)
objgraph.show_growth(limit=5)
Enter fullscreen mode Exit fullscreen mode

These tools reveal which objects keep accumulating.


3. Common Leak Patterns & Fixes

Pattern Symptoms Fix
Global caches that never purge Growing dict size Use functools.lru_cache with maxsize or explicit eviction
Reference cycles involving __del__ GC does not collect Remove __del__, use weakref.finalize
Unclosed file/network handles File descriptors leak Wrap resources in with statements
Large NumPy arrays retained by closures Memory spikes after batch Break closure references or copy‑on‑write

Example: Breaking a reference cycle

import weakref

class Node:
    def __init__(self, value):
        self.value = value
        self.parent = None
        self.children = []

    def set_parent(self, parent):
        self.parent = weakref.ref(parent)  # weak reference prevents a cycle
Enter fullscreen mode Exit fullscreen mode

4. Production‑Ready Safeguards

  1. Enable GC debugging
   import gc
   gc.set_debug(gc.DEBUG_LEAK)
Enter fullscreen mode Exit fullscreen mode
  1. Periodically force collection (e.g., every 10 minutes)
   if time.time() - last_gc > 600:
       gc.collect()
Enter fullscreen mode Exit fullscreen mode
  1. Monitor heap growth with Prometheus + process_resident_memory_bytes metric.
  2. Deploy a watchdog container that restarts the service if RSS exceeds a threshold.

5. Automated Patch Toolkit

We've bundled the most effective fixes into a ready‑to‑run script. Download the pre‑configured script here, Get the complete patch tool, or Access the full repository fix to apply the recommendations with a single command.


Conclusion

Fixing memory leaks in production requires a systematic approach: reproduce, profile, isolate, and apply targeted fixes. By integrating the diagnostics above and using the provided automation script, you can keep your Python services performant and resilient.

Happy debugging!

Top comments (0)