Ответ
Утечки памяти в Python обычно связаны с объектами, на которые сохраняются ненужные ссылки, что мешает сборщику мусора их удалить. Для их обнаружения и анализа используются следующие инструменты:
-
tracemalloc(встроенный модуль) Отслеживает блоки памяти, выделенные Python, и предоставляет статистику по местам их выделения (файл, строка, трейсбек). Почему: Это стандартный и мощный инструмент для точного определения источника утечки.import tracemalloc tracemalloc.start() # ... код, в котором предполагается утечка ... data = [i for i in range(10000)] snapshot = tracemalloc.take_snapshot() top_stats = snapshot.statistics('lineno') print("[ Top 10 ]") for stat in top_stats[:10]: print(stat) -
objgraphБиблиотека для визуализации графа объектов в памяти. Особенно полезна для поиска циклических ссылок, которые не может собрать сборщик мусора (хотя современные GC с этим справляются лучше). Почему: Помогает понять, какие объекты ссылаются друг на друга и почему они не удаляются.import objgraph # Показывает 10 самых распространенных типов объектов objgraph.show_most_common_types() # Находит объект по типу и рисует граф ссылок на него x = [] y = [x] x.append(y) objgraph.show_backrefs([x], max_depth=5) -
memory-profilerПрофилировщик, который позволяет отслеживать потребление памяти построчно для конкретной функции. Почему: Идеален для оптимизации памяти в "горячих" участках кода и для наглядной демонстрации роста потребления памяти.# Использование через декоратор from memory_profiler import profile @profile def my_func(): a = [1] * (10 ** 6) b = [2] * (2 * 10 ** 7) del b return a if __name__ == '__main__': my_func()Запуск из консоли:
python -m memory_profiler my_script.py -
gc.get_objects()иgc.get_referrers()Встроенные функции модуляgcдля низкоуровневого анализа. Позволяют получить список всех отслеживаемых объектов и найти объекты, которые ссылаются на данный. Почему: Для глубокого ручного анализа и отладки сложных случаев.
Типичный рабочий процесс: Начать с tracemalloc или memory-profiler для локализации проблемы, а затем использовать objgraph для детального анализа ссылок на проблемные объекты.
Ответ 18+ 🔞
А, слушай, вот эта хуйня с утечками памяти в Python — это ж классика, блядь! Сидишь такой, пишешь код, всё вроде летает, а потом — бац! — оперативка кончилась, и программа накрылась медным тазом. А всё потому, что какие-то ёбаные объекты болтаются в памяти, ссылаются друг на друга, и сборщик мусора нихуя не может сделать, руки у него короткие, блядь.
Ну, ладно, не будем о грустном. Смотри, какие у нас есть инструменты, чтобы эту заразу вычислить и придушить.
Первый наш боец — tracemalloc. Это встроенный модуль, ёпта, не надо ничего ставить. Он как слежка за каждым байтом: запоминает, где и какая память выделилась. Прям палочка-выручалочка, когда нужно понять, какая строчка кода жрёт память, как не в себя.
import tracemalloc
tracemalloc.start()
# ... код, в котором предполагается утечка ...
data = [i for i in range(10000)]
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
print("[ Top 10 ]")
for stat in top_stats[:10]:
print(stat)
Запустишь это — и тебе вывалится топ-10 мест, где память хуячили больше всего. Прям как детектив, блядь, улики на блюдечке.
Второй инструмент — objgraph. Вот это уже для настоящих расследований, когда подозреваешь, что объекты устроили ёбаный круговорот ссылок в природе и не хотят умирать. Хотя, честно, современный сборщик мусора с циклами вроде как справляется, но всякое бывает, особенно если там свои __del__ методы накручены.
import objgraph
# Показывает 10 самых распространенных типов объектов
objgraph.show_most_common_types()
# Находит объект по типу и рисует граф ссылок на него
x = []
y = [x]
x.append(y)
objgraph.show_backrefs([x], max_depth=5)
Эта команда show_backrefs — это просто песня, блядь! Она нарисует тебе такую схему связей, что ты офигеешь. Увидишь, кто на кого ссылается, и поймёшь, почему твой объект не может спокойно отойти в мир иной. Иногда такое насмотришься — волосы дыбом встают.
Третий вариант — memory-profiler. Это когда нужно не просто найти утечку, а посмотреть, как память растёт по строчкам внутри функции. Прям как кардиограмма, только для оперативки.
# Использование через декоратор
from memory_profiler import profile
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a
if __name__ == '__main__':
my_func()
Навесил декоратор @profile — и всё, функция теперь под колпаком. Запускаешь скрипт через python -m memory_profiler my_script.py и получаешь отчёт: вот тут съели столько, тут освободили столько. Нагляднее некуда, блядь.
Ну и на десерт — родной модуль gc. Это уже для самых отчаянных, кто готов копаться в кишках. Функции gc.get_objects() и gc.get_referrers() — это как скальпель и пинцет. Первая покажет тебе всех, кто плавает в памяти, а вторая — кто конкретно держит за шкирку твой проблемный объект.
Как с этим всем работать, спросишь? А логика простая, как три копейки, блядь.
- Сначала бьёшь по площадям — запускаешь
tracemallocилиmemory-profiler. Находишь подозрительное место, где память утекает, как вода в песок. - Потом, когда локализовал врага, включаешь
objgraphи смотришь, что это за хитрая жопа такая, кто её держит и почему она не хочет удаляться. - Ну а если совсем хардкор — лезешь с
gc.get_referrers()и разматываешь этот клубок ссылок вручную.
Вот и весь сказ, блядь. Главное — не паниковать, а методично проверять. А то бывает, начнёшь искать утечку, а там оказывается, что ты просто забыл соединение с базой закрыть, и оно висит, сука, открытым. Эх, в рот меня чих-пых!