gzx0 2020-04-23
ByteBuf释放不当容易造成内存泄漏。
一般情况下,业务handler中使用到的ByteBuf可以分为两类,请求ByteBuf和响应ByteBuf。如下:
public class MyServerHandler extends SimpleChannelInboundHandler<ByteBuf>{ @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { //服务器回送数据给客户端, 回送一个随机id , ByteBuf responseByteBuf = Unpooled.copiedBuffer(UUID.randomUUID().toString() + " ", Charset.forName("utf-8"));//这里UnpooledHeapByteBuf ctx.writeAndFlush(responseByteBuf); } }
msg即为请求ByteBuf,而responseByteBuf即为响应ByteBuf。
响应ByteBuf(如writeAndFlush(buf))由netty释放,例如在将HeapBuf转换为DirectBuf的时候释放。
protected final ByteBuf newDirectBuffer(ByteBuf buf) { final int readableBytes = buf.readableBytes(); if (readableBytes == 0) { ReferenceCountUtil.safeRelease(buf); return Unpooled.EMPTY_BUFFER; } final ByteBufAllocator alloc = alloc(); if (alloc.isDirectBufferPooled()) { ByteBuf directBuf = alloc.directBuffer(readableBytes); directBuf.writeBytes(buf, buf.readerIndex(), readableBytes); ReferenceCountUtil.safeRelease(buf);//将作为HeapByteBuf的buf引用计数-1,更新状态 return directBuf; } final ByteBuf directBuf = ByteBufUtil.threadLocalDirectBuffer(); if (directBuf != null) { directBuf.writeBytes(buf, buf.readerIndex(), readableBytes); ReferenceCountUtil.safeRelease(buf); return directBuf; } // Allocating and deallocating an unpooled direct buffer is very expensive; give up. return buf; }
请求ByteBuf,Handler继承SimpleChannelInboundHandler可释放;
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { boolean release = true; try { if (acceptInboundMessage(msg)) { @SuppressWarnings("unchecked") I imsg = (I) msg; channelRead0(ctx, imsg);//业务handler只需重写channelRead0 } else { release = false; ctx.fireChannelRead(msg); } } finally { if (autoRelease && release) { ReferenceCountUtil.release(msg);//引用计数-1 } } }
请求ByteBuf,fireChannelRead(),由TailContext释放。
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) {onUnhandledInboundMessage(ctx, msg);} protected void onUnhandledInboundMessage(ChannelHandlerContext ctx, Object msg) { onUnhandledInboundMessage(msg); if (logger.isDebugEnabled()) { logger.debug("Discarded message pipeline : {}. Channel : {}.", ctx.pipeline().names(), ctx.channel()); } } protected void onUnhandledInboundMessage(Object msg) { try { logger.debug( "Discarded inbound message {} that reached at the tail of the pipeline. " + "Please check your pipeline configuration.", msg); } finally { ReferenceCountUtil.release(msg);//引用计数-1 } }
对于请求ByteBuf,当业务handler没有继承SimpleChannelInboundHandler,没有fireChannelRead,也没有ReferenceCountUtil.release的话,这时就要考虑内存溢出的情况了。
所以应该时刻记着由最后一个使用的人释放(ReferenceCountUtil.release(byteBuf))。