Slide 1

Slide 1 text

Garbage collector and a bit of memory management Cyril @notorca Lashkevich

Slide 2

Slide 2 text

Модели управления памятью • Вручную • Подсчет ссылок • Garbage Collector

Slide 3

Slide 3 text

Ручное управление • Переменные на стеке • alloca • Масивы переменной длинны (C99)

Slide 4

Slide 4 text

Подсчет ссылок • retain(INCREF)/ release(DECREF) • autoreleasepool • weak

Slide 5

Slide 5 text

DAG (directed acyclic graph) • Задача программиста не допускать циклов в графе объектов

Slide 6

Slide 6 text

Что делать если циклы нужны? • Пример: граф с циклами • Приходится устранять циклы вручную

Slide 7

Slide 7 text

Weak-ссылки • Не увеличивают счетчик ссылок объекта • Автоматически зануляются при уничтожении объекта

Slide 8

Slide 8 text

Python sys.getrefcount() import sys! ! one = []! print 'At start :', sys.getrefcount(one)! two = one! print 'Second reference :', sys.getrefcount(one)! del two! print 'After del :', sys.getrefcount(one) At start : 2! Second reference : 3! After del : 2

Slide 9

Slide 9 text

Анализ графа объектов • Функции для анализа в модуле gc • gc.get_referrers(*objs)! • gc.get_referents(*objs)! • Native-объекты должны предоставлять метод tp_traverse

Slide 10

Slide 10 text

Модуль weakref • Слабые ссылки с callback на удаление объекта • Proxy-объекты бросающие исключение при использовании удаленного объекта (not hashable!) • WeakValue и WeakKey словари • WeakSet (3.2)

Slide 11

Slide 11 text

In [11]: obj = ExpensiveObject() In [12]: r = weakref.ref(obj) In [15]: print('obj:', obj) obj: <__main__.ExpensiveObject object at 0x108ea24d0> In [16]: print('ref:', r) ref: In [17]: print('r():', r()) r(): <__main__.ExpensiveObject object at 0x108ea24d0> In [18]: del obj Deleting Expencive object In [19]: print('r():', r()) r(): None

Slide 12

Slide 12 text

Ограничения • Не все типы могут быть weak: string, list, tuple... • Native-объекты должны реализовывать поддержку (через tp_weaklistoffset ) • Осторожно с weak словарями!

Slide 13

Slide 13 text

Garbage Collector • Удаляет объекты, недостижимые по ссылкам из корневого объекта но с ненулевым счетчиком ссылок

Slide 14

Slide 14 text

GC в python • Запускается когда колличество созданых объектов поколения превышает количество удаленных на х (по умолчанию 700, 10, 10) • Либо вручную gc.collect()! • Low memory, idle не инициируют сборку мусора • Может работать долго

Slide 15

Slide 15 text

Generations and thresholds • 3 "поколения" (списка) объектов: 0, 1, 2 • Новые объекты попадают в 0 (не все) • Сборка мусора работает по поколениям • Объекты пережившие сборку мусора переносятся в поколение +1 • Поколение 1 проверяется после threshold1 обработок поколения 0

Slide 16

Slide 16 text

Ограничения GC • Объекты с определенным __del__ не уничтожаются GC (он не знает в каком порядке их вызвать) • Native-объекты которые могут содержать ссылки должны правильно реализовывать tp_traverse

Slide 17

Slide 17 text

Проблемы с __del__ • Может создать циклические ссылки с sys.exc_traceback и sys.last_traceback (их нужно явно занулить) • Исключения в __del__ игнорируются с выводом сообщения на stderr • Не вызывается при завершении программы

Slide 18

Slide 18 text

Удаление неудаляемых объектов • Вызвать gc.collect() что бы явно запустить сборку мусора (вернет количество удаленных объектов • В gc.garbage список объектов с циклическими ссылками и __del__. Проходим по этому списку и разрываем циклические ссылки. (gc.get_referrers) • del gc.garbage[:]

Slide 19

Slide 19 text

Отладка • gc.DEBUG_LEAK, gc.DEBUG_*! • Heapy (http://guppy-pe.sourceforge.net/) • Meliae (https://launchpad.net/meliae) • gdb-heap (https://fedorahosted.org/gdb-heap/)

Slide 20

Slide 20 text

Альтернативы • PyPy • Отключить GC вообще и следить за деревом объектов вручную • Отключить но вызывать gc.collect() в правильные моменты времени

Slide 21

Slide 21 text

Спасибо