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))。