HTTP服务器的净内存泄漏。什么时候发布消息?

时间:2018-07-05 08:41:30

标签: java netty

除非使用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中所述,该项目的上一代将增加,事件为ReferenceCountUtil.release(msg)

  1. 如果未释放http输入消息,为什么official example没有明确调用release?
  2. channelRead中,如果在另一个bean中设置了接收到的msg,然后ctx.fireChannelRead传递了该bean,那么我应该像上面的代码一样为该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);
}

2 个答案:

答案 0 :(得分:3)

1。也许是一个错误。老实说,我不知道。但是如果您不调用Handler中的 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将在发送对象后释放

答案 1 :(得分:1)

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

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

因为每个净值处理程序都是负责任的链模式,所以write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)实际上可以具有任何对象msg(沿着链传递的参数)。在这种情况下,只需手动调用释放:

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

FullHttpResponse的实例化最终调用了refCnt加1的ByteBuffer.allocate

如果在以下处理程序中,由于异常或userFiredEvents,此FullHttpResponse不会通过调用发出:

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

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