Найти - Пользователи
Полная версия: Ретрансляция udp multicast в http на Twisted
Начало » Network » Ретрансляция udp multicast в http на Twisted
1 2
Андрей Светлов
Целых 17 “зажатых” объектов. Должно быть 0. gc.garbage пустой, если перед этим не вызывал gc.set_debug(gc.DEBUG_LEAK)
init
Точно, теперь я в gc.garbage вижу как раз вот эти data, которые приходят в datagramReceived. Но я так понял, если эта цифра 17 не увеличивается, то и память не должна съедаться?
Андрей Светлов
не совсем. Эти семнадцать объектов - “застрявшие”. Garbage collector не может их освободить (хотя и должен бы). Что “под ними” и кого они еще “держат” - не всегда видно. Но факт, что в “чистой” программе gc.collect() всегда возвращает 0.
Вывод - у тебя есть кольца (циклические ссылки).
init
Что интересно, вариант с deferred ест память в несколько раз медленнее..

Хотя нет, ошибся. Очень неравномерно растет, буду проверять.
Андрей Светлов
Ищи места, где нужно вставлять слабые ссылки (модуль weakref в помощь).
Еще можешь выложить свой пример с подробным описанием, как его завести (как я понимаю, слушаешь что-то вроде RTP и переправляешь пакеты через TCP). С указанием программ - источника медиа данных и конечного приемника. И указаниями, как их быстро поставить-настроить-запустить. (Примечание - у меня Винда, и серверного линукса нынче под рукой нет). Если все будет - посмотрю в чем дело, когда буду свободен.
Сейчас на работе дел много и мастер-класс на exception послезавтра…
P.S. Burus (burus.org) на последнем exception говорил, что одно их приложение на twisted тоже ело память как не в себя. После внимательного осмотра и небольших корректировок прочно сидит в положеных 30 MB. Мой опыт полностью это подтверждает.
init
Кажется решил проблему. В этом мне помогла ссылка http://www.smira.ru/2008/02/09/python-memory-leak-resolved/ где такая же причина утечки. Клиент не успевает забирать данные и они копятся в каком-то буфере. Сам буфер я не нашел, поэтому пришлось писать напрямую в сокет:

    def datagramReceived(self, data, address):
        # получаем пакет и посылаем его по всем затребовавшим
        for out in outputs:
            out.transport.socket.send(self.data)
            #out.write(data)

В таком виде память не съедается. Причина непосредственно утечки, как мне кажется, кроется в методе writeSomeData класса Connection из internet.tcp. Код выглядит так:

    def writeSomeData(self, data):
        """Connection.writeSomeData(data) -> #of bytes written | CONNECTION_LOST
        This writes as much data as possible to the socket and returns either
        the number of bytes read (which is positive) or a connection error code
        (which is negative)
        """
        try:
            # Limit length of buffer to try to send, because some OSes are too
            # stupid to do so themselves (ahem windows)
            return self.socket.send(buffer(data, 0, self.SEND_LIMIT))
        except socket.error, se:
            if se.args[0] == EINTR:
                return self.writeSomeData(data)
            elif se.args[0] in (EWOULDBLOCK, ENOBUFS):
                return 0
            else:
                return main.CONNECTION_LOST

Если я все правильно понял, то если в данный момент в буфере сокета уже есть неотправленные данные, то socket.send генерирует исключение, в одном из которых эта функция рекурсивно вызывается снова. То есть если появится клиент с низкой скоростью, то быстро наплодится очень много вызовов этой функции. Не знаю, прав я или нет, но других причин найти не смог.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB