@EugeneVKruglov

Как использовать Tracemalloc для оптимизации использования памяти?

Бот на питоне потребляет все больше памяти. БД SQLite3 размером примерно 350-400 МБ. В течение недели выделение памяти под него может дойти до 200-250 МБ и больше. Бот развернут на сервере под Линукс (Дебиан 11), в котором я вообще не разбираюсь, памяти в системе всего 1 ГБ, так что многовато забирает одна не самая сложная задача. Это примерно выглядит так:
память вначале
6669b31d7efa4789822244.jpeg
память через 7-9 дней
6669b358ecd3e507969251.jpeg

Хотел бы выяснить причину такого жора памяти, но не могу понять, с чего начать?
Как использовать tracemalloc? В частности, как заставить его вывести изменения потребления памяти только для модулей моего проекта, а не для всех подряд модулей питона? Догадываюсь, что для этого нужно задать параметры для фильтров snapshot.filter_traces, как в коде ниже, но как внести в них список своих файлов?
Код
def display_top(snapshot, key_type='lineno', limit=10):
    S = ''
    snapshot = snapshot.filter_traces((
        tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
        tracemalloc.Filter(False, "<unknown>"),
    ))
    top_stats = snapshot.statistics(key_type)
    S = f'Top {limit}'
    for index, stat in enumerate(top_stats[:limit], 1):
        frame = stat.traceback[0]
        size = round(stat.size / 1024, 2)
        S = f'{S}\r\n{index}: {frame.filename}: line {frame.lineno}, size: {size} KiB'
        line = linecache.getline(frame.filename, frame.lineno).strip()
        if line:
            S = f'{S}\r\n    {line}'
    other = top_stats[limit:]
    if other:
        size = round(sum(stat.size for stat in other) / 1024, 2)
        count = len(other)
        S = f'{S}\r\n{count} other: {size} KiB'
    total = round(sum(stat.size for stat in top_stats) / 1024, 2)
    S = f'{S}\r\nTotal allocated size: {total} KiB'
    return S

Или, может быть, что-то нужно сделать с Линукс, чтобы снизить расход памяти? Спасибо заранее.
Прошу сделать скидку на то, что я не профессионал.
  • Вопрос задан
  • 382 просмотра
Пригласить эксперта
Ответы на вопрос 4
Смотреть потребление памяти сразу после запуска неправильно. Надо смотреть как минимум после того, как бот обработает 5-10 запросов (причем если в боте есть разные команды, то надо чтобы он обработал все из них).
Советую недельку понаблюдать за памятью, только смотреть (условно) раз в день и записывать значения. Если потребление памяти в какой-то момент перестанет расти, то это значит, что никакой утечки нет и это нормальное потребление памяти для вашего бота. Если это потребление для вас слишком большое, то тогда нужно смотреть, что конкретно эту память потребляет и как это потребление уменьшить.
Если же потребление растет постоянно в течение длительного времени, то есть утечка и надо ее исправлять. В питоне утечка памяти скорее всего будет из-за того, что бот сохраняет что-то в глобальные переменные в процессе работы. Если такого нет, то утечка в какой-нибудь библиотеке. В любом случае, это без кода не понять.
Ответ написан
ValdikSS
@ValdikSS
Скорее всего, у вас glibc malloc не отдаёт память.
Если запуск следующей команды освободит память, то дело точно в этом:
sudo gdb --batch -ex 'call malloc_trim(0)' --pid PID_OF_PYTHON_PROCESS

Где PID_OF_PYTHON_PROCESS — pid процесса.

Решений несколько: от задания MALLOC_ARENA_MAX=2 MALLOC_TRIM_THRESHOLD_=65536 до использования альтернативных аллокаторов, например, jemalloc.
Ответ написан
fenrir1121
@fenrir1121
Начни с документации
Как использовать Tracemalloc для оптимизации использования памяти?

А в чем причина такого подхода к решению проблемы?

Если растёт потребление памяти, значит вы ее не освобождаете. Да питон это делает за вас, но думать об этом все равно нужно потому что для очистки ему требуется чтобы на объект никто не ссылался.

Берёте любой профилировщик, например memray и ищите где проблема в вашем коде.
Ответ написан
mayton2019
@mayton2019
Bigdata Engineer
Вообще не факт что дело в Питоне. Вместе с ботом запускается SQLite. Как часть процесса.
База сложным образом кешируется и обладает свойством забирать столько сколько надо
с учетом настроек. Я-бы посмотрел настройки SQLite.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы