Netty ByteBuf泄露定位修改。

1. ByteBuf

2. 问题描述

日志记录中报堆外内存溢出。

3. 问题定位及修改

Netty提供了ByteBuf泄露的检测机制。

JVM启动参数中添加: -Dio.netty.leakDetectionLevel=advanced , log4j2.xml配置io.netty日志记录即可。

禁用(DISABLED) – 完全禁止泄露检测,省点消耗。
简单(SIMPLE)   – 默认等级,告诉我们取样的1%的ByteBuf是否发生了泄露,但总共一次只打印一次,看不到就没有了。
高级(ADVANCED) – 告诉我们取样的1%的ByteBuf发生泄露的地方。每种类型的泄漏(创建的地方与访问路径一致)只打印一次。对性能有影响。
偏执(PARANOID) – 跟高级选项类似,但此选项检测所有ByteBuf,而不仅仅是取样的那1%。对性能有绝大的影响。

检测到如下泄露点,

举例1

 13:29:25.273 [MODBUS_MESSAGE_POOL-thread-14] [] [] [] ERROR 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.
Created at:
    io.netty.buffer.AdvancedLeakAwareByteBuf.writeBytes(AdvancedLeakAwareByteBuf.java:604)
    io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:849)
    com.pinnet.protocol.hwmodbus.message.ModbusMessageRunnable.run(ModbusMessageRunnable.java:46)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    java.lang.Thread.run(Thread.java:748)

 可以看到readBytes()创建后,没有释放。

修改:

buffer.readBytes(4).release();

举例2:

2018-06-30 13:29:25.278 [MESSAGE_POOL-thread-14] [] [] [] ERROR 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.
    #1:
    io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:273)
    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.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
    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.handler.ssl.SslHandler.unwrap(SslHandler.java:1380)
    io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1159)
    io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1194)
    io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489)
    #2:
    io.netty.buffer.AdvancedLeakAwareByteBuf.getUnsignedShort(AdvancedLeakAwareByteBuf.java:172)
    io.netty.handler.codec.LengthFieldBasedFrameDecoder.getUnadjustedFrameLength(LengthFieldBasedFrameDecoder.java:469)
    io.netty.handler.codec.LengthFieldBasedFrameDecoder.decode(LengthFieldBasedFrameDecoder.java:417)
    com.pinnet.protocol.hwmodbus.tcp.MobusLengthDecoder.decode(MobusLengthDecoder.java:32)
    #3:
    io.netty.buffer.AdvancedLeakAwareByteBuf.order(AdvancedLeakAwareByteBuf.java:70)
    io.netty.handler.codec.LengthFieldBasedFrameDecoder.getUnadjustedFrameLength(LengthFieldBasedFrameDecoder.java:462)
    io.netty.handler.codec.LengthFieldBasedFrameDecoder.decode(LengthFieldBasedFrameDecoder.java:417)
    com.pinnet.protocol.hwmodbus.tcp.MobusLengthDecoder.decode(MobusLengthDecoder.java:32)
    io.netty.handler.codec.LengthFieldBasedFrameDecoder.decode(LengthFieldBasedFrameDecoder.java:343)
    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)
#4:
        Hint: 'decoder' will handle the message from this point.
#5:
        Hint: 'idleHandler' will handle the message from this point.

 修改:解码器中释放Bytebuf in

public class MobusLengthDecoder extends LengthFieldBasedFrameDecoder {

    public MobusLengthDecoder(ByteOrder byteOrder, int maxFrameLength,
            int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment,
            int initialBytesToStrip, boolean failFast) {
        super(byteOrder, maxFrameLength, lengthFieldOffset, lengthFieldLength,
                lengthAdjustment, initialBytesToStrip, failFast);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx,
            io.netty.buffer.ByteBuf in) throws Exception {

        ByteBuf buf = (ByteBuf) super.decode(ctx, in);
        // LEAK: ByteBuf.release() was not called before it's garbage-collected.
        in.release();

        ...
    }

}
原文地址:https://www.cnblogs.com/eaglediao/p/7004404.html