netty 解码小坑

1. in.readBytes 导致堆外内存泄漏

使用netty 中偶现 LEAK: ByteBuf.release() was not called before it's garbage-collected. 在几次检查自己的代码,发现ByteBuf都有释放。就是找不到问题,翻阅了一下netty的官方文档,有两种方式可以 打印出泄漏的详细信息

  1. ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
  2. -Dio.netty.leakDetectionLevel=advanced
    打印日志如下:
    io.netty.buffer.AdvancedLeakAwareByteBuf.writeBytes(AdvancedLeakAwareByteBuf.java:600)
    io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:829)
    com.XXX.XXX.XXX.network.codec.XXXTcpDecoder.decode(XXXTcpDecoder.java:55)
    io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489)
    io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428)
    io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
    io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
    io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644)
    io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579)
    io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496)
    io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458)
    io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
    io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
    java.lang.Thread.run(Thread.java:748)
Created at:
    io.netty.util.ResourceLeakDetector.track(ResourceLeakDetector.java:237)
    io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:331)
    io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:181)
    io.netty.buffer.AbstractByteBufAllocator.buffer(AbstractByteBufAllocator.java:117)
    io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:828)
    com.XXX.XXX.XXX.network.codec.XXXTcpDecoder.decode(XXXTcpDecoder.java:55)
    io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489)
    io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428)
    io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
    io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
    io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644)
    io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579)
    io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496)
    io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458)
    io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
    io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
    java.lang.Thread.run(Thread.java:748)

com.XXX.XXX.XXX.network.codec.XXXTcpDecoder.decode(XXXTcpDecoder.java:55
来看一下这一行明确提示泄漏的地方,然后看了一下对应的代码一脸懵逼


netty 解码小坑_第1张图片
image.png

in.readBytes 需要释放bytebuf ???? 这他妈什么操作 吓得我赶紧看了一下源代码。。 注意 我特么也很sb 仔细看入参 一个是 long 一个是 byte[] 导致我浪费了很多时间

  @Override
    public ByteBuf readBytes(byte[] dst) {
        readBytes(dst, 0, dst.length);
        return this;
    }

没毛病啊,非常奇怪。为啥!!!!! 然后就是各种google
加了 ReferenceCountUtil.release(in);

    in.readByte();
    int dataLength = remaining - 17;
    byte[] dataByte = new byte[dataLength];
    in.readBytes(dataByte);
    String data = new String(dataByte);
    in.readBytes(8);
    。。。。。
    ReferenceCountUtil.release(in);

不但没用,链接都报错了!!!!!
接着开始怀疑这个是不是netty自身的bug。。。
换了版本还是有问题!!!!! 受不了!!!!!
然后继续思考人生。无意再次点击看源码。。突然有一句mmp 不知当讲不当讲!!!

 @Override
    public ByteBuf readBytes(int length) {
        checkReadableBytes(length);
        if (length == 0) {
            return Unpooled.EMPTY_BUFFER;
        }

        ByteBuf buf = alloc().buffer(length, maxCapacity);
        buf.writeBytes(this, readerIndex, length);
        readerIndex += length;
        return buf;
    }

.... readBytes方法返回新的byteBuf ???


netty 解码小坑_第2张图片
image.png

那特么我刚开始看是哪个源码??? 然后把整个 AbstractByteBuf 看了一遍
内心崩溃 readBytes 方法有很多,每个入参不同,但是!!!!!!
readBytes(int length) 这个是返回一个新的bytebuf 所以需要释放掉!!!
弄了半天,还是自己sb。从错误中已经明确提示哪一行没有释放,但是结果还是饶了这么多弯。。。。。。

你可能感兴趣的:(netty 解码小坑)