Как мне найти то, что использует память в процессе Python в производственной системе? - PullRequest
37 голосов
/ 26 сентября 2008

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

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

Ответы [ 6 ]

33 голосов
/ 05 марта 2012

Используя gc интерфейс сборщика мусора в Python и 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", 'wb')
    xs = []
    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__)
            xs.append({'id': i, 'class': cls, 'size': size, 'referents': referents})
    cPickle.dump(xs, dump)

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

with open("memory.pickle", 'rb') as dump:
    objs = 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:
        xs = []
        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__)
                xs.append({'id': i, 'class': cls, 'size': size, 'referents': referents})
        cPickle.dump(xs, dump)
5 голосов
/ 27 сентября 2008

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

2 голосов
/ 31 января 2010

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

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

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

2 голосов
/ 27 сентября 2008

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

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

2 голосов
/ 27 сентября 2008

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

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

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

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

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

Подсчет ссылок на отладку

1 голос
/ 27 сентября 2008

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

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

...