Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Обнаружение утечек памяти в Python
Утечка памяти возникает, когда программа удерживает объекты в памяти, которые больше не используются. В Python с автоматической сборкой мусора это сложнее, чем в C++, но возможно.
Как возникают утечки в Python
Основные причины:
# ❌ Циклические ссылки
class Node:
def __init__(self):
self.next = None
a = Node()
b = Node()
a.next = b
b.next = a # Циклическая ссылка
del a, b # Не очищаются из памяти!
# ❌ Глобальные списки, которые растут
leak_list = []
def add_item():
leak_list.append([0] * 1000000) # Растёт бесконечно
# ❌ Замыкания с большими объектами
def create_function(large_data):
def inner():
return len(large_data) # large_data остаётся в памяти
return inner
big_list = [0] * 1000000
func = create_function(big_list)
del big_list # Но func всё ещё удерживает ссылку
# ❌ Observer pattern без unsubscribe
class Observable:
def __init__(self):
self.observers = []
def subscribe(self, observer):
self.observers.append(observer)
observable = Observable()
for i in range(1000000):
observable.subscribe(lambda: print(i))
# observers растёт бесконечно
Способ 1: memory_profiler (простой способ)
Установка:
pip install memory-profiler
Использование:
from memory_profiler import profile
@profile
def leaky_function():
leak = []
for i in range(1000):
leak.append([0] * 10000) # Растущая утечка
return sum(len(x) for x in leak)
if __name__ == "__main__":
leaky_function()
Запуск:
python -m memory_profiler script.py
Вывод:
Line # Mem usage Increment Occurences Line Contents
================================================
4 37.2 MiB 0.0 MiB 1 @profile
5 37.2 MiB 0.0 MiB 1 def leaky_function():
6 37.2 MiB 0.0 MiB 1 leak = []
7 167.4 MiB 130.2 MiB 1000 for i in range(1000):
8 167.4 MiB 0.0 MiB 1000 leak.append([0] * 10000)
Способ 2: tracemalloc (встроенный)
Встроённый модуль Python:
import tracemalloc
tracemalloc.start()
# Код, который может иметь утечку
leak = []
for i in range(1000):
leak.append([0] * 10000)
# Снимок памяти
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
print("[ Top 10 ]")
for stat in top_stats[:10]:
print(stat)
Вывод:
script.py:7: 130.2 MiB
leak.append([0] * 10000)
Более детальный анализ:
import tracemalloc
tracemalloc.start()
# Первый снимок
leak = []
for i in range(500):
leak.append([0] * 10000)
snapshot1 = tracemalloc.take_snapshot()
# Добавляем ещё памяти
for i in range(500):
leak.append([0] * 10000)
snapshot2 = tracemalloc.take_snapshot()
# Сравниваем разницу
top_stats = snapshot2.compare_to(snapshot1, 'lineno')
for stat in top_stats[:5]:
print(stat)
Способ 3: objgraph (анализ объектов)
Установка:
pip install objgraph
Основное использование:
import objgraph
import gc
# Основные объекты в памяти
objgraph.show_most_common_types(limit=20)
# Вывод:
# MyClass 1000
# dict 500
# list 300
Отслеживание роста объектов:
import objgraph
objgraph.show_growth()
# Код
leak = []
for i in range(1000):
leak.append({})
objgraph.show_growth() # Показывает, что появилось новое
# Вывод:
# dict 1000 +1000
# list 1 +1
Отслеживание утечки циклических ссылок:
import objgraph
import gc
class Node:
def __init__(self):
self.ref = None
# Создаём циклическую ссылку
a = Node()
b = Node()
a.ref = b
b.ref = a
del a, b
gc.collect()
# Ищем мусор с циклическими ссылками
objgraph.show_most_common_types()
Способ 4: Мониторинг в реальном времени
import psutil
import os
process = psutil.Process(os.getpid())
# Текущее использование памяти
info = process.memory_info()
print(f"RSS: {info.rss / 1024 / 1024:.2f} MB") # Физическая память
print(f"VMS: {info.vms / 1024 / 1024:.2f} MB") # Виртуальная память
# Детальная информация
mem_percent = process.memory_percent()
print(f"Memory: {mem_percent:.2f}%")
Мониторинг в цикле:
import psutil
import os
import time
process = psutil.Process(os.getpid())
for i in range(10):
leak = []
for j in range(1000):
leak.append([0] * 10000)
mem = process.memory_info().rss / 1024 / 1024
print(f"Iteration {i}: {mem:.2f} MB")
time.sleep(0.1)
Способ 5: Анализ графа объектов
import gc
import sys
class MyClass:
pass
obj = MyClass()
obj.self_ref = obj # Циклическая ссылка
# Получаем рефернцы на объект
referrers = gc.get_referrers(obj)
print(f"Object is referenced by {len(referrers)} objects")
for ref in referrers:
print(f" - {type(ref)}")
# Получаем объекты, на которые ссылается наш объект
referees = gc.get_referents(obj)
print(f"Object references {len(referees)} objects")
Способ 6: Тестирование утечек
import unittest
import gc
import tracemalloc
class TestMemoryLeak(unittest.TestCase):
def setUp(self):
tracemalloc.start()
self.baseline = tracemalloc.take_snapshot()
def tearDown(self):
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.compare_to(self.baseline, 'lineno')
# Проверяем, что не выросла память больше чем на 1 МБ
total = sum(stat.size_diff for stat in top_stats)
self.assertLess(total, 1024 * 1024, f"Memory increased by {total / 1024 / 1024:.2f} MB")
def test_no_leak(self):
# Это должно быть без утечки
for i in range(100):
data = [1, 2, 3, 4, 5]
del data
Инструменты и пакеты
| Инструмент | Цель | Установка |
|---|---|---|
| memory_profiler | Построчный анализ | pip install memory-profiler |
| tracemalloc | Встроенный анализ | встроенный |
| objgraph | Анализ объектов | pip install objgraph |
| pympler | Глубокий анализ | pip install pympler |
| psutil | Мониторинг процесса | pip install psutil |
| guppy3 | Heap анализ | pip install guppy3 |
Исправление утечек
Циклические ссылки:
# ❌ Утечка
class Node:
def __init__(self):
self.next = None
# ✅ Исправление: используй weakref
import weakref
class Node:
def __init__(self):
self.next = None
def set_next(self, node):
self.next = weakref.ref(node)
Вывод из памяти:
# ✅ Явно удаляй большие объекты
big_data = [0] * 1000000
# работа с big_data
del big_data
# ✅ Закрывай ресурсы
with open("file.txt") as f:
data = f.read()
# ✅ Отписывайся от событий
observable.unsubscribe(observer)
Лучшие практики
- Регулярно мониторьте память в production
- Избегайте циклических ссылок или используйте
weakref - Тестируйте на утечки в CI/CD
- Используйте контекстные менеджеры (
with) для ресурсов - Профилируйте перед оптимизацией
- Помните о замыканиях — они могут удерживать большие объекты
Обнаружение утечек требует системного подхода и правильных инструментов.