Почему подсчет ссылок FullHttpResponse ведет себя по-разному, в зависимости от базовой реализации ByteBuf? - PullRequest
2 голосов
/ 03 апреля 2020

У меня проблемы с утечками на EmbeddedChannel, который я использую для анализа необработанного ответа http в FullHttpResponse. Во время анализа я заметил, что подсчет ссылок ByteBuf, который я посылаю в канал как «вход», ведет себя по-разному в зависимости от его реализации, что выглядит странно.

Для справки, вот метод, который я использую для анализа необработанный ввод:

private FullHttpResponse decodeHttpResponse(ByteBuf responseBuff) {
    EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseDecoder(), new HttpObjectAggregator(99999999));
    try {
        channel.writeInbound(responseBuff);
        channel.flush();
        channel.finish();
        return channel.readInbound();
    } finally {
        channel.close();
        channel.finishAndReleaseAll();
    }
}

Давайте использовать простое ByteBuf, которое я создаю из ответа http, который я ранее записал в файл, и проведу некоторое тестирование, вот мой вывод (числа - это счетчики ссылок объекта) :

>>>---------- Test with ByteBuf ------------<<<
>>> [buf]  : 1
buf.retain()
>>> [buf]  : 2
resp = decodeHttpResponse(cbuf) => io.netty.handler.codec.http.HttpObjectAggregator$AggregatedFullHttpResponse
>>> [buf]  : 2                  // [1]
>>> [resp] : 1                  // 
resp.retain()
>>> [buf]  : 2
>>> [resp] : 2
resp.release()
>>> [buf]  : 2
>>> [resp] : 1
resp.release()                  //
>>> [buf]  : 1                  // [2]
>>> [resp] : 0                  //
  • [1]: Когда мы создаем FullHttpResponse, refCount на входе ByteBuf равен без изменений .
  • [2] : Когда FullHttpResponse освобождается (refCount достигает 0), он распространяет один единственный релиз на базовый ByteBuf .

OK, теперь давайте обернем этот входной ByteBuf в CompositeByteBuf и использовать его для построения FullHttpResponse:

>>>---------- Test with CompositeByteBuf ------------<<<
>>> [buf]  : 1
buf.retain()
>>> [buf]  : 2
cbuf = buf.alloc().compositeBuffer()
>>> [buf]  : 2
>>> [cbuf] : 1
cbuf.addComponent(true, buf)
>>> [buf]  : 2
>>> [cbuf] : 1
cbuf.retain()
>>> [buf]  : 2
>>> [cbuf] : 2
resp = decodeHttpResponse(cbuf) => io.netty.handler.codec.http.HttpObjectAggregator$AggregatedFullHttpResponse
>>> [buf]  : 2              //
>>> [cbuf] : 1              // [1]
>>> [resp] : 1              //
resp.retain()
>>> [buf]  : 2
>>> [cbuf] : 1
>>> [resp] : 2
resp.release()
>>> [buf]  : 2
>>> [cbuf] : 1
>>> [resp] : 1
resp.release()
>>> [buf]  : 2              //
>>> [cbuf] : 1              // [2]
>>> [resp] : 0              //
  • [1]: когда мы создаем FullHttpResponse, refCount на входном ByteBuf уменьшается на на 1 .
  • [2]: когда освобождается FullHttpResponse ( refCount достигает 0), он не распространяется на базовый CompositeByteBuf .

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

...