http 服务器的 Netty 内存泄漏。什么时候发布消息?
Netty memory leak for http server. When to release message?
除非使用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
,但如 所述,该项目的 Old Gen 增加,事件 ReferenceCountUtil.release(msg)
。
- 如果http输入消息没有释放,为什么official example没有显式调用释放?
- 在
channelRead
中,如果received msg设置在另一个bean中,然后这个bean被ctx.fireChannelRead
传递过来,我是否应该像上面的代码一样为这个msg调用release?
- 如果
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?
}
- write操作呢,write对象也要调用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);
}
1.Maybe它是一个bug.Honestly,我不know.But如果你不调用它必须释放obj ctx.fireChannelRead() 在你的处理程序中(中断处理程序链)。或者 TailContext 将释放对象。
2.You 无法释放此 Handler 中的 msg,如果这样做,可能其他上下文将分配此中使用的 obj context.You 只有当 bean 结束时才应释放对象;
3.Your 新对象未实现 ReferenceCounted,为什么你需要释放对象?只是 return false 但在 [=19 中什么都不做=]ReferenceCountUtil如下:
public static boolean release(Object msg) {
if (msg instanceof ReferenceCounted) {
return ((ReferenceCounted) msg).release();
}
return false;
}
4.You不需要释放write object因为netty会在发送object后释放
经过几个小时的实验和调试到源码中,关于第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)
实际上可以有任何对象消息(沿着链传递的参数)。这种情况只需要手动调用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,它不会发送出去。从客户端的角度来看,请求挂起。
除非使用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
,但如 ReferenceCountUtil.release(msg)
。
- 如果http输入消息没有释放,为什么official example没有显式调用释放?
- 在
channelRead
中,如果received msg设置在另一个bean中,然后这个bean被ctx.fireChannelRead
传递过来,我是否应该像上面的代码一样为这个msg调用release? - 如果
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?
}
- write操作呢,write对象也要调用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);
}
1.Maybe它是一个bug.Honestly,我不know.But如果你不调用它必须释放obj ctx.fireChannelRead() 在你的处理程序中(中断处理程序链)。或者 TailContext 将释放对象。
2.You 无法释放此 Handler 中的 msg,如果这样做,可能其他上下文将分配此中使用的 obj context.You 只有当 bean 结束时才应释放对象;
3.Your 新对象未实现 ReferenceCounted,为什么你需要释放对象?只是 return false 但在 [=19 中什么都不做=]ReferenceCountUtil如下:
public static boolean release(Object msg) {
if (msg instanceof ReferenceCounted) {
return ((ReferenceCounted) msg).release();
}
return false;
}
4.You不需要释放write object因为netty会在发送object后释放
经过几个小时的实验和调试到源码中,关于第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)
实际上可以有任何对象消息(沿着链传递的参数)。这种情况只需要手动调用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,它不会发送出去。从客户端的角度来看,请求挂起。