2009-08-27 4 views
24

У меня есть небольшой многопоточный скрипт, работающий в django, и со временем его запуск использует все больше и больше памяти. Оставляя его на целый день, он ест около 6 ГБ ОЗУ, и я начинаю заменять.Python: Отладка утечки памяти

После http://www.lshift.net/blog/2008/11/14/tracing-python-memory-leaks я вижу это как наиболее распространенных типов (только с 800M памяти, используемой):

(Pdb) objgraph.show_most_common_types(limit=20) 
dict      43065 
tuple      28274 
function     7335 
list      6157 
NavigableString   3479 
instance     2454 
cell      1256 
weakref     974 
wrapper_descriptor   836 
builtin_function_or_method 766 
type      742 
getset_descriptor   562 
module      423 
method_descriptor   373 
classobj     256 
instancemethod    255 
member_descriptor   218 
property     185 
Comment     183 
__proxy__     155 

который ничего странного не показывают. Что мне теперь делать, чтобы помочь отладить проблемы с памятью?

Обновление: Попытка некоторых вещей, которые люди рекомендуют. Я запускал программу за одну ночь, и когда я работаю, используется 50% * 8G == 4G ОЗУ.

(Pdb) from pympler import muppy 
(Pdb) muppy.print_summary() 
            types | # objects | total size 
========================================== | =========== | ============ 
            unicode |  210997 |  97.64 MB 
             list |  1547 |  88.29 MB 
             dict |  41630 |  13.21 MB 
             set |   50 |  8.02 MB 
             str |  109360 |  7.11 MB 
            tuple |  27898 |  2.29 MB 
             code |  6907 |  1.16 MB 
             type |   760 | 653.12 KB 
            weakref |  1014 |  87.14 KB 
             int |  3552 |  83.25 KB 
        function (__wrapper__) |   702 |  82.27 KB 
         wrapper_descriptor |   998 |  77.97 KB 
             cell |  1357 |  74.21 KB 
    <class 'pympler.asizeof.asizeof._Claskey |  1113 |  69.56 KB 
         function (__init__) |   574 |  67.27 KB 

Это не суммируется с 4G и не дает мне никаких больших данных, структурированных для исправления. Юникод - из набора() «done» узлов, и список выглядит как случайный weakref s.

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

Ни один из объектов, которые я использовал, имеет метод __del__, и, просматривая библиотеки, он не похож на django и python-mysqldb. Любые другие идеи?

+0

"работает в Django"? Вы имеете в виду, что вы используете веб-сервер Django для выполнения дополнительной обработки фоновой обработки без веб-сервиса? Рассматривали ли вы расщепление этого не-веб-материала в отдельный процесс? –

+2

Это задание cron, которое импортирует Django settgings.py и использует многие функции Django ORM. Таким образом, он не создается сервером, но использует многие функции (что могло бы быть уместным). –

ответ

31

См. http://opensourcehacker.com/2008/03/07/debugging-django-memory-leak-with-trackrefs-and-guppy/. Короткий ответ: если вы используете django, но не в формате, основанном на веб-запросе, вам нужно вручную запустить db.reset_queries() (и, конечно же, DEBUG = False, как упомянули другие). Django автоматически делает reset_queries() после веб-запроса, но в вашем формате это никогда не происходит.

+2

db.reset_queries() решил проблему для меня, большое спасибо. – pyeleven

0

Пробег: Guppy.

В основном вам нужна дополнительная информация или вы сможете их извлечь. Guppy даже предоставляет графическое представление данных.

1

Думаю, вы должны использовать разные инструменты. По-видимому, статистика, которую вы получили, - это только объекты GC (т. Е. Объекты, которые могут участвовать в циклах); в частности, ему не хватает струн.

Я рекомендую использовать Pympler; это должно предоставить вам более подробную статистику.

+0

top показывает мое приложение, используя 7% * 8GB = 560M. pympler.muppy.print_summary() показывает около 55M. Где остальные? –

6

Вы попробовали gc.set_debug()?

Вы должны задать себе простые вопросы:

  • Могу ли я с помощью объектов с __del__ методами? Нужно ли мне абсолютно, недвусмысленно, в них?
  • Могу ли я получить ссылочные циклы в моем коде? Разве мы не можем разбить эти круги, прежде чем избавиться от объектов?

См, основной вопрос будет циклом объектов, содержащих __del__ методы:

import gc 

class A(object): 
    def __del__(self): 
     print 'a deleted' 
     if hasattr(self, 'b'): 
      delattr(self, 'b') 

class B(object): 
    def __init__(self, a): 
     self.a = a 
    def __del__(self): 
     print 'b deleted' 
     del self.a 


def createcycle(): 
    a = A() 
    b = B(a) 
    a.b = b 
    return a, b 

gc.set_debug(gc.DEBUG_LEAK) 

a, b = createcycle() 

# remove references 
del a, b 

# prints: 
## gc: uncollectable <A 0x...> 
## gc: uncollectable <B 0x...> 
## gc: uncollectable <dict 0x...> 
## gc: uncollectable <dict 0x...> 
gc.collect() 

# to solve this we break explicitely the cycles: 
a, b = createcycle() 
del a.b 

del a, b 

# objects are removed correctly: 
## a deleted 
## b deleted 
gc.collect() 

Я действительно рекомендую вам флаг объекты/концепции, которые задействуя в приложении, и сосредоточиться на свою жизнь: когда они вам больше не нужны, есть ли у нас что-нибудь, ссылающееся на него?

Даже для циклов без __del__ методы, мы можем иметь вопрос:

import gc 

# class without destructor 
class A(object): pass 

def createcycle(): 
    # a -> b -> c 
    #^  | 
    # ^<--<--<--| 
    a = A() 
    b = A() 
    a.next = b 
    c = A() 
    b.next = c 
    c.next = a 
    return a, b, b 

gc.set_debug(gc.DEBUG_LEAK) 

a, b, c = createcycle() 
# since we have no __del__ methods, gc is able to collect the cycle: 

del a, b, c 
# no panic message, everything is collectable: 
##gc: collectable <A 0x...> 
##gc: collectable <A 0x...> 
##gc: collectable <dict 0x...> 
##gc: collectable <A 0x...> 
##gc: collectable <dict 0x...> 
##gc: collectable <dict 0x...> 
gc.collect() 

a, b, c = createcycle() 

# but as long as we keep an exterior ref to the cycle...: 
seen = dict() 
seen[a] = True 

# delete the cycle 
del a, b, c 
# nothing is collected 
gc.collect() 

Если вы должны использовать «видели» -как словари, или истории, будьте осторожны, что вы держите только фактические данные, необходимые , и никаких внешних ссылок на него нет.

Я немного разочарован set_debug, я бы хотел, чтобы он мог быть сконфигурирован для вывода данных где-то еще, кроме stderr, но, надеюсь, that should change soon.

+0

gc.collect() возвращает все как коллекционные, а при втором вызове возвращает 0. Это означает, что у меня нет никаких циклов вправо? –

+0

@Paul: Нет, у вас все еще есть циклы. Посмотрите на последний пример, который я дал: здесь gc.collect() возвращает 0, и ничего не печатается. Если у вас есть циклы объектов, которые не имеют методов __del__, gc останется спокойным. – NicDumZ

1

Вы используете любое расширение? Они являются прекрасным местом для утечек памяти и не будут отслеживаться инструментами python.

+0

Нет расширений, но хорошее место для других, спотыкающихся здесь, чтобы посмотреть. –

+0

Если вы используете Django ORM, вы используете драйвер базы данных DB-API. Это MySQLdb? Текущая версия имеет известную утечку памяти курсора, когда соединение установлено с использованием use_unicode = True (что имеет место для Django> = 1.0). – zgoda

+0

Да, вы правы на деньги! Я использую все это. Любое известное решение? –

19

Действительно ли DEBUG = False в settings.py?

Если Django не с удовольствием сохранит все SQL-запросы, которые вы создаете, добавляет.

+2

Вау, я знал, что писать слова django там помогут. Да, мой сценарий не использовал мои настройки production.py. * Смущенно *. Посмотрим, устраняет ли проблема памяти. –

+0

Вот оно! DEBUG, установленный в True, действительно съедает много памяти при выборе из большой базы данных. –

Смежные вопросы