Подтвердить что ты не робот

Как найти то, что использует память в процессе Python в производственной системе?

Моя система производства иногда обнаруживает утечку памяти, которую я не смог воспроизвести в среде разработки. Я использовал профайлер памяти Python (в частности, Heapy) с некоторым успехом в среде разработки, но он не может помочь мне с вещами, которые я не могу воспроизвести, и я неохотно применяю нашу производственную систему с Heapy, потому что для ее выполнения требуется некоторое время, и ее многопоточный удаленный интерфейс не очень хорошо работает на нашем сервере.

То, что я думаю, что я хочу, - это способ сбрасывать моментальный снимок производственного процесса Python (или, по крайней мере, gc.get_objects), а затем анализировать его в автономном режиме, чтобы увидеть, где он использует память. Как получить базовый дамп процесса python вроде этого? Как только у меня его есть, как мне с ним что-то полезнее?

4b9b3361

Ответ 1

Используя Python gc интерфейс сборщика мусора и sys.getsizeof(), можно сбросить все объекты python и их размеры. Здесь код, который я использую для производства, для устранения утечки памяти:

rss = psutil.Process(os.getpid()).get_memory_info().rss
# Dump variables if using more than 100MB of memory
if rss > 100 * 1024 * 1024:
    memory_dump()
    os.abort()

def memory_dump():
    dump = open("memory.pickle", 'w')
    for obj in gc.get_objects():
        i = id(obj)
        size = sys.getsizeof(obj, 0)
        #    referrers = [id(o) for o in gc.get_referrers(obj) if hasattr(o, '__class__')]
        referents = [id(o) for o in gc.get_referents(obj) if hasattr(o, '__class__')]
        if hasattr(obj, '__class__'):
            cls = str(obj.__class__)
            cPickle.dump({'id': i, 'class': cls, 'size': size, 'referents': referents}, dump)

Обратите внимание, что я сохраняю данные только от объектов, у которых есть атрибут __class__, потому что это единственные объекты, о которых я забочусь. Должно быть возможно сохранить полный список объектов, но вам нужно будет позаботиться о выборе других атрибутов. Кроме того, я обнаружил, что получение ссылок для каждого объекта было очень медленным, поэтому я решил сохранить только референты. В любом случае, после аварии, полученные маринованные данные могут быть прочитаны следующим образом:

dump = open("memory.pickle")
while dump:
    obj = cPickle.load(dump)

Добавлено 2017-11-15

Версия Python 3.6 находится здесь:

import gc
import sys
import _pickle as cPickle

def memory_dump():
    with open("memory.pickle", 'wb') as dump:
        for obj in gc.get_objects():
            i = id(obj)
            size = sys.getsizeof(obj, 0)
            #    referrers = [id(o) for o in gc.get_referrers(obj) if hasattr(o, '__class__')]
            referents = [id(o) for o in gc.get_referents(obj) if hasattr(o, '__class__')]
            if hasattr(obj, '__class__'):
                cls = str(obj.__class__)
                cPickle.dump({'id': i, 'class': cls, 'size': size, 'referents': referents}, dump)

Ответ 2

Можете ли вы записать трафик (через журнал) на своем производственном сайте, а затем повторно воспроизвести его на своем сервере разработки с помощью отладчика памяти python? (Я рекомендую dozer: http://pypi.python.org/pypi/Dozer)

Ответ 3

Я не знаю, как сбросить полное состояние интерпретатора python и восстановить его. Было бы полезно, я буду следить за этим ответом, если у кого-то есть идеи.

Если у вас есть идея, в которой происходит утечка памяти, вы можете добавить проверки refcounts своих объектов. Например:

x = SomeObject()
... later ...
oldRefCount = sys.getrefcount( x )
suspiciousFunction( x )
if (oldRefCount != sys.getrefcount(x)):
    print "Possible memory leak..."

Вы также можете проверить, что количество ссылок больше, чем некоторое число, которое подходит для вашего приложения. Чтобы сделать это дальше, вы можете изменить интерпретатор python для выполнения этих видов проверки, заменив макросы Py_INCREF и Py_DECREF на свой собственный. Это может быть немного опасно в производственном приложении.

Вот эссе с дополнительной информацией об отладке подобных вещей. Он больше ориентирован на авторов плагинов, но большинство из них применимо.

Отладка ссылок <

Ответ 4

Создайте свой ядро ​​для дампа, затем клонируйте экземпляр программы в достаточно похожем поле, используя gdb. Есть специальные макросы, чтобы помочь с отладкой python-программ в gdb, но если вы можете получить свою программу одновременно обслуживает удаленную оболочку, вы можете просто продолжить выполнение программы и запросить ее с помощью python.

Мне никогда не приходилось это делать, поэтому я не уверен на 100%, что это сработает, но, возможно, указатели будут полезны.

Ответ 5

Meliae выглядит многообещающим:

Этот проект похож на heapy (в проекте guppy), пытаясь понять, как распределена память.

В настоящее время основное отличие заключается в том, что он разбивает задачу вычисления суммарной статистики и т.д. потребления памяти при фактическом сканировании потребления памяти. Он делает это, потому что я часто хочу выяснить, что происходит в моем процессе, в то время как мой процесс потребляет огромное количество памяти (1 ГБ и т.д.). Это также позволяет значительно упростить сканер, поскольку я не выделяю объекты python при попытке проанализировать потребление памяти объектов python.

Ответ 6

gc module имеет некоторые функции, которые могут быть полезны, например, перечисление всех объектов, которые сборщик мусора обнаружил недостижимым, но не может освободить, или список всех отслеживаемых объектов.

Если у вас есть подозрение, что объекты могут протечь, модуль weakref может быть полезен, чтобы узнать, собраны ли/когда объекты.