由于 Netty 中的 ByteBuffers 导致的内存泄漏

Posted

技术标签:

【中文标题】由于 Netty 中的 ByteBuffers 导致的内存泄漏【英文标题】:Memory leak due to ByteBuffers in Netty 【发布时间】:2018-06-08 12:18:28 【问题描述】:

我创建了一个小型 Netty 服务器来计算 BigInteger 的阶乘并发送结果。代码如下。

Factorial.java

public class Factorial 

    private int port;

    public Factorial(int port) 
        this.port = port;
    

    public void run(int threadcount) throws Exception 
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup(threadcount);
        try 
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioserverSocketChannel.class)
             .childHandler(new ChannelInitializer<SocketChannel>() 
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception 
                     ch.pipeline().addLast(new FactorialHandler());
                 
             )
             .option(ChannelOption.SO_BACKLOG, 128)          
             .childOption(ChannelOption.SO_KEEPALIVE, true); 

            ChannelFuture f = b.bind(port).sync(); 

            f.channel().closeFuture().sync();
         finally 
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        
    

    public static void main(String[] args) throws Exception 
        int port = 15000;
        new Factorial(port).run(Integer.parseInt(args[0]));
    

FactorialHandler.java

public class FactorialHandler extends ChannelInboundHandlerAdapter 

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)  
        BigInteger result = BigInteger.ONE;
        String resultString;
        for (int i=2000; i>0; i--)
            result = result.multiply(BigInteger.valueOf(i));
        resultString = result.toString().substring(0, 3)+"\n";
        ByteBuf buf = Unpooled.copiedBuffer(resultString.getBytes());
        ctx.write(buf);
        ctx.flush();
    

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) 
        cause.printStackTrace();
        ctx.close();
    

运行时出现以下错误

Jun 08, 2018 5:28:09 PM io.netty.util.ResourceLeakDetector reportTracedLeak
SEVERE: 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:

如给定链接中所述,我通过在ctx.flush() 之后的channelRead 方法中调用buf.release() 释放了ByteBuffer。

但是当我这样做时,服务器开始抛出以下异常

io.netty.util.IllegalReferenceCountException: refCnt: 0, increment: 1

谁能告诉我如何解决这个问题?

【问题讨论】:

也许这对您有帮助 netty.io/wiki/user-guide-for-4.x.html,这里 Object msg 本身已发布。 @Override public void channelRead(ChannelHandlerContext ctx, Object msg) // (2) // Discard the received data silently. ((ByteBuf) msg).release(); // (3) 【参考方案1】:

问题不在于出站 ByteBuf。出站 ByteBuf 始终为您处理(请参阅OutboundMessages)。问题是入站 ByteBuf。我在看着你,FactorialHandler。它扩展了ChannelInboundHandlerAdapter。请注意 JavaDoc 中的这一点:

请注意,消息不会在 channelRead(ChannelHandlerContext, Object) 方法返回 自动地。如果您正在寻找 ChannelInboundHandler 自动释放收到的消息的实现, 请看SimpleChannelInboundHandler。

您的处理程序有这样的签名:

public void channelRead(ChannelHandlerContext ctx, Object msg)

那个 msg,(顺便说一下,你不使用它)实际上是一个 ByteBuf,这正是上面的 JavaDoc 注释警告你的。 (在没有任何其他 ChannelHandler 的情况下,消息将始终是 ByteBuf 的实例。)

所以你的选择是:

    使用 SimpleChannelInboundHandler,它将为您清理该引用。 在处理程序结束时,使用 ReferenceCountUtil.release(java.lang.Object msg) 释放入站 ByteBuf。

【讨论】:

谢谢@Nicholas。添加 ReferenceCountUtil.release(msg) 解决了问题【参考方案2】:

因为你没有调用msg.release()(msg 是 ByteBuf 的一个实例)。

【讨论】:

结果不是 BigInteger 类的实例吗?

以上是关于由于 Netty 中的 ByteBuffers 导致的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

多线程 ByteBuffers 比顺序慢?

为啥 ByteBuffers hashCodes 是一样的?

ByteBuffers 是在 Android 中存储 > 1MB 数据的好方法吗?

[Netty在Apache ServiceComb] 中的实践问题答疑

从 Netty 5 中的多路复用通道交付

哪些 JVM 不支持直接 java.nio.ByteBuffer?