Правильный способ минимизировать использование памяти с помощью Netty

У меня есть два сценария в Netty, где я пытаюсь минимизировать количество копий памяти и оптимизировать использование памяти:

(1) Чтение очень большого кадра (20 Мегабит).

(2) Чтение большого количества очень маленьких кадров (20 мегабит по 50 бит на кадр) для перестройки в одно сообщение на более высоком уровне конвейера.

Для первого сценария, когда я получаю длину в начале кадра, я расширил FrameDecoder. К сожалению, поскольку я не вижу, как вернуть длину Netty (я только указываю, завершен ли кадр или нет), я полагаю, что Netty проходит несколько циклов заполнения буфера, копирования и перераспределения, таким образом используя больше памяти, чем требуется. Есть ли что-то, что мне здесь не хватает? Или мне следует полностью избегать FrameDecoder, если я ожидаю такого сценария?

Во втором сценарии я в настоящее время создаю связанный список всех маленьких фреймов, которые я оборачиваю с помощью ChannelBuffers.wrappedBuffer (который затем я могу обернуть в ChannelBufferInputStream), но я снова использую гораздо больше памяти, чем ожидал (возможно, потому что в выделенных ChannelBuffers есть свободное место?). Это правильный способ использования Netty ChannelBuffers?


person Gareth    schedule 19.11.2011    source источник


Ответы (2)


  1. Существует специализированная версия декодера кадров, которая называется LengthFieldBasedFrameDecoder. Это удобно, когда у вас есть заголовок с длиной сообщения. Он может даже извлечь длину сообщения из заголовка, указав смещение.

  2. На самом деле ChannelBuffers.wrappedBuffer не создает копии полученных данных, он создает составной буфер из заданных буферов, поэтому ваши данные полученного кадра не будут скопированы. Если вы держите составные буферы/свою пользовательскую оболочку в коде и забыли обнулить, могут произойти утечки памяти.

Это практики, которым я следую,

  • Выделяйте прямые буферы для долгоживущих объектов, разделяйте их при использовании.

  • когда я хочу объединить/закодировать несколько буферов в один большой буфер. Я использую ChannelBuffers.wrappedBuffer

  • Если у меня есть буфер и я хочу что-то сделать с ним/его частью, я делаю его срез, вызывая slice или slice(0,..) в экземпляре буфера канала

  • Если у меня есть буфер канала и я знаю позицию данных, которая мала, я всегда использую методы getXXX

  • Если у меня есть буфер канала, который используется во многих местах, чтобы сделать из него что-то, всегда делайте его модифицируемым, нарезайте его при использовании.

Примечание: channelbuffer.slice не копирует данные, он создает буфер канала с новым индексом чтения и записи.

person Jestan Nirojan    schedule 21.11.2011
comment
Я ценю ответ, но вы в значительной степени указываете, что я уже делаю. LengthFieldBasedFrameDecoder, поскольку он расширяет Frame Decoder, наследует его проблемы (вы не можете избежать циклов заполнения буфера/перераспределения и копирования). Я знаю, что wrapBuffer не создает копии, но я по-прежнему использую намного больше памяти, чем ожидалось (куча увеличилась в 3-4 раза по сравнению с размером сообщения). - person Gareth; 22.11.2011
comment
@Gareth Не видя кода, трудно что-либо сказать. Я только что проверил FrameDecoder и обнаружил, что он использует буфер DynamicChannel, размер которого начинается с 256 байт, поэтому я не уверен, как он будет расти, когда вы получите 20 МБ. , я думаю, лучше написать свою версию FrameDecoder с фиксированным буфером. - person Jestan Nirojan; 22.11.2011

В конце концов, оказалось, что лучший способ решить мою проблему с FrameDecoder — написать свой собственный поверх SimpleChannelUpstreamHandler. Как только я определил длину из заголовка, я создал ChannelBuffer с размером, точно соответствующим длине. Это (наряду с другими изменениями) значительно улучшило производительность памяти моего приложения.

person Gareth    schedule 18.01.2012