<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Andrey Smirnov's Blog (памяти)</title><link>http://www.smira.ru/</link><description></description><language>en</language><lastBuildDate>Sun, 11 Jan 2015 19:24:27 GMT</lastBuildDate><generator>http://getnikola.com/</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Поиск несуществующего memory leak</title><link>http://www.smira.ru/en/posts/20080209python-memory-leak-resolved.html</link><dc:creator>Andrey</dc:creator><description>&lt;p&gt;&lt;/p&gt;&lt;p&gt;Очень забавная история. Был работающий и написанный сервер вещаний (a-la Red5) на Python. В связи с развитием основной класс - класс протокола - был разрезан на несколько более мелких классов, которые собирались вместе множественным наследованием в единый монолит, который должен был по функциональности повторять исходный класс (до рефакторинга). Как показало вскрытие, старая версия стабильно работает неделю за неделей, а новая версия через неопределенный, но достаточно короткий период времени (несколько часов) откушивает всю доступную память и погибает.&lt;/p&gt;
&lt;p&gt;Типичная ситуация для memory leak, причём опытным путём стало понятно, что эта утечка как-то коррелирует с нагрузкой на сервер (что логично), но зависимость неоднозначная. Момент появления утечки был сужен до одного коммита - казалось бы дело за малым. Анализ исходников в поисках того момента, когда была внесена утечка ничего дает - исходники испаханы вдоль и поперек. Ничего.&lt;/p&gt;
&lt;p&gt;Анализ во время выполнения с помощью с помощью довольно замысловатых утилит из &lt;a href="http://wingolog.org/archives/2007/11/27/reducing-the-footprint-of-python-applications"&gt;этого поста&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ни к чему не привёл - нет ничего. Я пробовал считать количество объектов, которые видит модуль &lt;code&gt;gc&lt;/code&gt; в питоне (то есть тех, кто поддерживает &lt;em&gt;garbage collection&lt;/em&gt;), но количество не растет существенно и пропорционально нагрузке на сервер. Однако память процесс кушает и довольно быстро.&lt;/p&gt;
&lt;p&gt;В проекте было расширение, написанное на Си, - его анализ, исправление мелких неточностей результата не дает.&lt;/p&gt;
&lt;p&gt;Остается неутешительный вывод - в силу того, что число объектов в программе не растет, то значит либо растет размер каких-то объектов (например, строк путем конкатенации), либо это какая-то бага в Python, ОС или где-то еще. &lt;/p&gt;
&lt;p&gt;Последнее выглядит романтично и привлекательно, но на самом деле ничего не дает - такую ошибку еще тяжелее найти и поправить, однако багтрекеры FreeBSD, Python и Twisted молчат по поводу таких ошибок.&lt;/p&gt;
&lt;p&gt;Двигаемся дальше - создается тестовый клиент вещаний, который имитирует нагрузку на сервер: разное количество вещаний, авторы, подписчики вещания, различные ошибочные ситуации, чат. Тест, запущенный локально с локальным сервером (а нелокально тестовое окружение собрать тяжело - поток порядка 50-100 Мбит/с) не дает результатов - никаких следов утечки памяти ни за час, ни за два.&lt;/p&gt;
&lt;p&gt;Отчаяние, практически неделя потерянного впустую времени, тестирование различных вариантов... Что еще? Есть еще &lt;a href="http://twistedmatrix.com/"&gt;Twisted-Conch+Twisted-Manhole&lt;/a&gt; - живая консоль с интерпретатором питона в работающем сервере. Но и она не дает никаких результатов - garbage collection работает, никаких разумных объектов, которые могли бы дать утечку памяти нет.&lt;/p&gt;
&lt;p&gt;Озарение как всегда приходит в тот момент, когда его совсем не ждешь: ведь сервер вещает, то есть получает на вход поток в 0,5 Мбит/с, раздает его 200 клиентам и делает на выходе 100 Мбит/с, если у кого-то из этих 200 клиентов канал меньше по ширине 0,5 Мбит/с, то данные в буферах процессах начнут скапливаться. Для этого есть решение - дроп пакетов, он  был реализован и работал...&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Эврика&lt;/em&gt;! После разделения большого класса на кучу мелких фрагментов некоторые методы "разрезались" на части. И в них надо поставить вызов метода предка, чтобы все "куски" метода отработали, как и раньше. Но ведь в питоне есть еще &lt;a href="http://www.python.org/download/releases/2.3/mro/"&gt;MRO&lt;/a&gt; (Method Resolution Order), и надо отвыкнуть от того, что базовый класс в моём ощущении является последним в цепочке, то есть у него нет &lt;code&gt;super(Klass, self)&lt;/code&gt; - он есть! Просто метод, который включал анализ буфера записи, который в свою очередь вовремя включал механизм пропуска пакетов отключился. И хотя всё работало, но пропуск пакетов не включался и буферы записи росли неограниченно. Исправление в одной строке - вызова метода предка решило проблему. Обидно :)&lt;/p&gt;</description><guid>http://www.smira.ru/en/posts/20080209python-memory-leak-resolved.html</guid><pubDate>Sat, 09 Feb 2008 20:45:53 GMT</pubDate></item></channel></rss>