2
votes

I am encountering a ResourceLeakDetector exception in my DecodeEventHandler class that extends ReplayingDecoder. I am having difficulty understanding when and where I should release any of the method's BytBuf. Do I need to release the passed ByteBuf? I tried releasing the header, eventBody, and attachedData ByteBuf objects but that created problems later on in my code. The code is...

protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception
{
    switch (state())
    {
        case READ_HEADER:
            ByteBuf header = byteBuf.readBytes(BaseEvent.EVENT_HEADER_SIZE);
            newEvent = new BaseEvent(header.nioBuffer());
            checkpoint(EventDecoderState.READ_BODY);
            // Fall through

        case READ_BODY:
            ByteBuf eventBody = byteBuf.readBytes(newEvent.getEventHeaderBodySize() - BaseEvent.EVENT_HEADER_SIZE);
            newEvent.setEventBody(eventBody.nioBuffer());
            checkpoint(EventDecoderState.READ_ATTACHED_DATA);
            // Fall through

        case READ_ATTACHED_DATA:
            ByteBuf attachedData = byteBuf.readBytes(newEvent.getAttachedDataSize());
            newEvent.clearAttachedData();
            newEvent.addAttachedData(attachedData.nioBuffer());
            list.add(newEvent);
            checkpoint(EventDecoderState.READ_HEADER);
    }
}

The method starts at line 23. The exception I an receiving is...

2016-06-07 13:30:49.783 [ERROR] (nioEventLoopGroup-7-1) io.netty.util.ResourceLeakDetector  - LEAK: ByteBuf.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information.
Recent access records: 2
#2:
    io.netty.buffer.AdvancedLeakAwareByteBuf.nioBuffer(AdvancedLeakAwareByteBuf.java:669)
    com.oakgate.netty.DecodeEventHandler.decode(DecodeEventHandler.java:30)
    io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:376)
    io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:245)
    io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:154)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:354)
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:145)
    io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
    io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:154)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:354)
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:145)
    io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:266)
    io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:154)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:354)
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:145)
    io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:1078)
    io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:117)
    io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:527)
    io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:484)
    io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:398)
    io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:370)
    io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:742)
    io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:145)
    java.lang.Thread.run(Thread.java:745)
#1:
    io.netty.buffer.AdvancedLeakAwareByteBuf.writeBytes(AdvancedLeakAwareByteBuf.java:561)
    io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:790)
    io.netty.handler.codec.ReplayingDecoderByteBuf.readBytes(ReplayingDecoderByteBuf.java:576)
    com.oakgate.netty.DecodeEventHandler.decode(DecodeEventHandler.java:29)
    io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:376)
    io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:245)
    io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:154)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:354)
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:145)
    io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
    io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:154)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:354)
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:145)
    io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:266)
    io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:154)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:354)
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:145)
    io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:1078)
    io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:117)
    io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:527)
    io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:484)
    io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:398)
    io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:370)
    io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:742)
    io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:145)
    java.lang.Thread.run(Thread.java:745)
Created at:
    io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:271)
    io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:179)
    io.netty.buffer.AbstractByteBufAllocator.buffer(AbstractByteBufAllocator.java:115)
    io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:789)
    io.netty.handler.codec.ReplayingDecoderByteBuf.readBytes(ReplayingDecoderByteBuf.java:576)
    com.oakgate.netty.DecodeEventHandler.decode(DecodeEventHandler.java:29)
    io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:376)
    io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:245)
    io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:154)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:354)
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:145)
    io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
    io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:154)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:354)
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:145)
    io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:266)
    io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:83)
    io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:154)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:354)
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:145)
    io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:1078)
    io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:117)
    io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:527)
    io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:484)
    io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:398)
    io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:370)
    io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:742)
    io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:145)
    java.lang.Thread.run(Thread.java:745)

Additional information

When I release each ByteBuf created by the call byteBuf.readBytes() (the header, eventBody, and attachedData) I encounter problems later on when the event is processed. I call nioBuffer() to create a ByteBuffer from the ByteBuf and it appears that releasing the original ByteBuf affects the ByteBuffer from the ioBuffer() call. According to the docs the returned buffer shares the content with the bytebuf which most likely means I cannot release it just yet. Am I going to need to retain each ByteBuf and then release when the event is processed? It appears that way unless there is some other way to do this.

1

1 Answers

2
votes

You're creating new ByteBufs via byteBuf.readBytes, they need to be released.

Example for your case READ_HEADER::

case READ_HEADER:
    ByteBuf header = null;
    try {
        header = byteBuf.readBytes(BaseEvent.EVENT_HEADER_SIZE);
        newEvent = new BaseEvent(header.nioBuffer());
        checkpoint(EventDecoderState.READ_BODY);
    } finally {
        if(header != null) 
            header.release();
    }
    // Fall through