Проблема с обнаружением утечки памяти (tracemalloc / objgraph / gc не помогает) - PullRequest
6 голосов
/ 15 мая 2019

У меня есть сетевой процесс, который связывается по TCP, собирая данные, десериализуя их и, наконец, сохраняя части в хранилище ключей / значений LevelDB (через Plyvel ).

Медленно со временем он будет использовать всю доступную память до такой степени, что вся моя система блокируется (Ubuntu 18.04). Я пытаюсь диагностировать причину, но у меня заканчиваются идеи, как продолжить расследование.

Основные подозреваемые, которые я имею в виду, - это потоки данных, с которыми мы работаем для десериализации объектов. Общее, что здесь сделано: получение данных от asyncio.StreamReader и вызов deserialize_from_bytes (см. Далее)

    @classmethod
    def deserialize_from_bytes(cls, data_stream: Union[bytes, bytearray]):
        """ Deserialize object from a byte array. """
        br = BinaryReader(stream=data_stream)
        inv_payload = cls()
        try:
            inv_payload.deserialize(br)
        except ValueError:
            return None
        finally:
            br.cleanup()
        return inv_payload

где BinaryReader инициализируется так

   def __init__(self, stream: Union[io.BytesIO, bytes, bytearray]) -> None:
        super(BinaryReader, self).__init__()

        if isinstance(stream, (bytearray, bytes)):
            self._stream = io.BytesIO(stream)
        else:
            self._stream = stream

и cleanup() - это удобная оболочка для self._stream.close()

Что я пробовал

  1. Я начал с демонстрации фрагмента top 10 из tracemalloc. В момент времени, когда /proc/$mypid/status показывает использование памяти в 2 ГБ (на VmSize), наиболее потребляющий элемент из tracemalloc сообщает всего 38 МБ, за которым следуют второе и третье - 3 МБ и 360 КБ.

    Это уже поднимает вопрос; где остальные ~ 1,995 ГБ?

  2. Не помогло вышеприведенным выводом, который я пробовал objgraph . Я врываюсь в процесс с pdb и использую

import objgraph
objgraph.show_most_common_types(limit=20)

чтобы получить

function                   16451
tuple                      11456
dict                       10371
weakref                    3058
list                       2893
cell                       2446
Traceback                  2277
Statistic                  2277
_Binding                   2109
getset_descriptor          1814
type                       1680
builtin_function_or_method 1469
wrapper_descriptor         1311
method_descriptor          1284
frozenset                  992
property                   983
module                     810
ModuleSpec                 808
SourceFileLoader           738
Attrs                      593

В этом списке я не вижу объектов, относящихся к моей программе (которые могут указывать на то, что ссылки не были освобождены). Из других objgraph примеров я обнаружил, что приведенные выше значения не кажутся необычными. Я проверил пару function объектов и всегда находил что-то похожее на это (связанное с asyncio), которое не предполагает утечки памяти.

(Pdb) objgraph.at(0x10f309b70)
<function _run_coroutine.<locals>.step_next.<locals>.continue_ at 0x10f309b70>

Пожалуйста, поправьте меня здесь, если я что-то упустил. Опять же, без шага дальше

  1. Последняя попытка выяснить, могу ли я принудительно освободить любую память, я вручную запускаю gc.collect () . Повторный вызов этого дает значения между 25-500 для числа недоступных объектов. Это не вызывает у меня проблемы (не так ли?). Я также попытался запустить программу с gc.set_debug(gc.DEBUG_LEAK), но это выдает настолько много информации, что я ничего не могу с этим поделать.

Какие-нибудь советы, что я могу попробовать здесь?

...