Netty - выделение списка буферов многократного использования через PooledByteBufAllocator - PullRequest
0 голосов
/ 06 июля 2018

Представьте себе приложение Netty, отправляющее данные из разных источников на один и тот же канал.

В отправленных данных / выходных байтах каждый источник будет идентифицирован определенным заголовком + данные из источника.

Я планирую использовать небольшой список буферов многократного использования, содержимое которых (байты) обновляется с каждым новым блоком данных, поступающим из данного источника. Этот подход небольшого набора многократно используемых буферов на источник данных должен сэкономить время на выделении прямых буферов и простой записи заголовков (данного источника) и новых данных в одну и ту же область памяти (каждого данного исходного буфера) снова и снова. Содержимое буферов затем отправляется / записывается в канал сокета из одного из двух упакованных (комбинированных) буферов (в зависимости от условия).

Я пробую этот подход, надеясь, что смогу создать 5 прямых буферов (блоков памяти) для пула внутри каждого класса DataSource и продолжать его повторное использование. Тем не менее, я получаю исключение при попытке использовать буферы в любом месте кода. Исключение: io.netty.util.IllegalReferenceCountException: refCnt: ​​0

public class DataSource {

    final List<ByteBuf> BUFFERS = new ArrayList<>();

    private void alocateHeaderBuffers() {

        ByteBufAllocator pbba = PooledByteBufAllocator.DEFAULT;
        BUFFERS.add(0, pbba.directBuffer(32, 32));    // buffer of fixed size  32 bytes - header 1
        BUFFERS.add(1, pbba.directBuffer(128, 128));  // buffer of fixed size 128 bytes - header 2
        BUFFERS.add(2, pbba.directBuffer(256));       // buffer of minimum size 256 (can grow) - for data of various size
        // Two combined buffers using - Unpooled.wrappedBuffer():
        // Combine 2 buffers: 0 + 2
        // and buffers:       1 + 2
        BUFFERS.add(3, Unpooled.wrappedBuffer(BUFFERS.get(0), BUFFERS.get(2)));
        BUFFERS.add(4, Unpooled.wrappedBuffer(BUFFERS.get(1), BUFFERS.get(2)));
    }
}

Описание исключения - если я делаю:

    ByteBuf bb1 = BUFFERS.get(0);
    bb1.writeBytes(headerPrefix); // headerPrefix = 4 bytes array

bb1 отображается отладчиком как «освобожденный» и следующая строка бросает: io.netty.util.IllegalReferenceCountException: refCnt: ​​0

НО если я попробую это в следующем буфере в списке:

    ByteBuf bb2 = BUFFERS.get(1);
    bb2.writeBytes(headerPrefix); // headerPrefix = 4 bytes array

это прекрасно работает. НО

    ByteBuf bb3 = BUFFERS.get(2);
    bb3.writeBytes(headerPrefix); // headerPrefix = 4 bytes array

терпит неудачу так же, как bb1.

Ответы [ 3 ]

0 голосов
/ 06 июля 2018

Когда вы говорите:

Этот подход небольшого набора буферов многократного использования на источник данных должен сэкономить время на выделении прямых буферов

, это именно то, что PooledByteBufAllocator.DEFAULT. Одна из причин, по которой он объединяет буферы, заключается в том, чтобы избежать длительного времени выделения для прямых ByteBuf с.

Таким образом, выделяя серию ByteBufs из распределенного распределителя, вы постоянно резервируете объединенные ресурсы, которые должны использоваться совместно.

Если вы намереваетесь продолжить свой подход, я бы просто предварительно выделил ваши буферы по умолчанию UnpooledByteBufAllocator , используя directBuffer (int)

0 голосов
/ 07 июля 2018

Вы также открыли ошибку в Netty Issue Tracker , в которой я объясняю, что я думаю, что она работает так, как описано в документации по Java. Я думаю, что вы правы, это может быть немного удивительно и трудно рассуждать, но опять же, это точно соответствует Javadocs.

Тем не менее, как уже упоминалось в выпуске, мы осудили этот метод и ввели замену, которая не дает такого «удивительного поведения». Это будет частью 4.1.26.Final.

См. https://github.com/netty/netty/pull/8040 и https://github.com/netty/netty/pull/8096.

0 голосов
/ 06 июля 2018

После дальнейшего тестирования возникает проблема. После выделения начальных 3 буферов и последующего использования:

 Unpooled.wrappedBuffer(BUFFERS.get(0), BUFFERS.get(2));
 System.out.println(bb1.refCnt()); // returns 0  !!!

и любой из нижележащих / ссылочных буферов (например, 0 и 2) пуст - нет записанных в них данных, пустые буферы освобождаются при вызове метода wrappedBuffer (). Refcount () в пустом буфере возвращает 0.

С другой стороны, если мы поместим некоторые данные в указанный буфер до того, как вызовем метод wrappedBuffer (), буфер не освободится и refcount () для этого буфера вернет 1.

 HEADER_BUFFERS.get(0).writeBytes(headerPrefix);
 Unpooled.wrappedBuffer(BUFFERS.get(0), BUFFERS.get(2));
 System.out.println(bb1.refCnt()); // returns 1  !!!

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

Дальнейшее исследование предлагает обходной путь / решение - используйте .retain ():

Unpooled.wrappedBuffer(BUFFERS.get(0).retain(), BUFFERS.get(2).retain());
...