【问题标题】:Netty memory leak for http server. When to release message?http 服务器的 Netty 内存泄漏。什么时候发布消息?
【发布时间】:2018-07-09 10:53:39
【问题描述】:

除非使用SimpleChannelInboundHandler channelRead0,否则如果ctx.fireChannelRead 未被调用,则应释放输入数据包。

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    log.error("receive {}", msg);
    if (msg instanceof FullHttpRequest) {            
        FullHttpRequest req = (FullHttpRequest) msg;
        ReferenceCountUtil.release(msg);
        clientChannel.writeAndFlush(new RequestWrapper(req, foo)); 
    }
}

clientChannel.writeAndFlush 成功时,将requestWrapper 推入队列。 netty 不显示LEAK warnings,但如jvm out of memory 中所述,该项目的Old Gen 增加,事件与ReferenceCountUtil.release(msg)

  1. 如果http输入消息没有释放,为什么official example没有显式调用释放?
  2. channelRead 中,如果接收到的 msg 设置在另一个 bean 中,然后这个 bean 由 ctx.fireChannelRead 传递,我是否应该为这个 msg 调用 release,就像上面的代码一样?
  3. 如果ctx.fireChannelRead(newObject) 传递了一个新对象,我应该在下一个处理程序中调用release(newObject) 吗?

像这样:

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    ctx.fireChannelRead("hello world");
}

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    assert "hello world".equals(msg);
    ReferenceCountUtil.release(msg); // is this necessary if it is created in the former handler?
}
  1. 写操作怎么样,写对象也要调用release吗?

像这样:

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    ctx.write("bye", promise);
}

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    assert "bye".equals(msg);
    // should I call ReferenceCountUtil.release(msg); ?
    ctx.write(“bye bye”, promise);
}

【问题讨论】:

    标签: java netty


    【解决方案1】:

    1.也许这是一个错误。老实说,我不知道。但是如果你不调用 ctx.fireChannelRead() 在你的处理程序中它必须释放 obj(中断处理程序的链)。或者 TailContext 将释放 obj。

    2.你不能释放这个Handler中的msg,如果你这样做了,可能其他上下文会分配这个上下文中使用的obj。你应该只在bean结束时释放对象;

    3.你的新对象没有实现ReferenceCounted,为什么你需要释放这个对象呢?只返回false,在ReferenceCountUtil中什么也不做,如下:

        public static boolean release(Object msg) {
        if (msg instanceof ReferenceCounted) {
            return ((ReferenceCounted) msg).release();
        }
        return false;
    }
    

    4.写对象不需要释放,因为netty发送对象后会释放

    【讨论】:

    • 我做了几个实验,我同意前3个。关于最后一个,我会打开一个答案来说明它。
    【解决方案2】:

    经过数小时的实验并调试到源代码,大约第 4 点: ReferenceCountUtil.refCnt:

    public static int refCnt(Object msg) {
        return msg instanceof ReferenceCounted ? ((ReferenceCounted) msg).refCnt() : -1;
    }
    

    因为每个 netty 处理程序都是一个负责任的链模式,write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) 实际上可以有任何对象 msg(沿链传递的参数)。这种情况只需要手动调用release即可:

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        FullHttpResponse fullHttpResponse = ...
        ctx.write(fullHttpResponse, promise);
    }
    

    FullHttpResponse 的实例化最终调用了 ByteBuffer.allocate,它带有一个 refCnt 加 1。

    如果在以下处理程序中,由于异常或userFiredEvents,这个FullHttpResponse不会被调用发送出去:

        ctx.write(msg, promise);
        ctx.flush();
    

    那么FullHttpResponse需要手动释放。最后但并非最不重要的一点是,如果FullHttpResponse 已释放 refCnt,它不会发送出去。从客户端的角度来看,请求挂起。

    【讨论】:

      猜你喜欢
      • 2011-05-13
      • 1970-01-01
      • 2012-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多