netty的異常 IllegalReferenceCountException refCnt: 0java
這是由於Netty有引用計數器的緣由,自從Netty 4開始,對象的生命週期由它們的引用計數(reference counts)管理,而不是由垃圾收集器(garbage collector)管理了。ByteBuf是最值得注意的,它使用了引用計數來改進分配內存和釋放內存的性能。ide
在咱們建立ByteBuf對象後,它的引用計數是1,當你釋放(release)引用計數對象時,它的引用計數減1,若是引用計數爲0,這個引用計數對象會被釋放(deallocate),並返回對象池。性能
當嘗試訪問引用計數爲0的引用計數對象會拋出IllegalReferenceCountException異常:.net
/** * Should be called by every method that tries to access the buffers content to check * if the buffer was released before. */ protected final void ensureAccessible() { if (checkAccessible && refCnt() == 0) { throw new IllegalReferenceCountException(0); } }
這個問題產生的最要緣由是在第一次發送完心跳請求後,ctx.write 等一系列方法調用了ByteBuf的release方法,將其引用計數減爲了0 致使的:netty
咱們主要看其方法棧中調用信息,獲得一個結論,是每次調用ctx.write/writeAndFlush, pipeline.write/writeAndFlush , 等一系列方法時,被封裝的ByteBuf對象的引用計數會減一。致使第二次使用一樣對象的包裝對象時,出現引用計數的問題。code
能夠調用 echoMsg.refCnt();
來獲取當前引用計數值. 在 ctx.write(...)
先後加一行打印, 就能夠發現, ctx.write(...)
完以後, 引用計數減小了1.對象
echoMsg.retain()
進行引用計數加1.例如:@Override protected void channelRead0(final ChannelHandlerContext ctx, final HttpContent msg) { System.out.println("收到" + msg); ByteBuf echoMsg = msg.content(); echoMsg.retain(); System.out.println(new String(ByteBufUtil.getBytes(echoMsg))); DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, echoMsg); response.headers().set("Content-Type", "text/plain"); ctx.write(response).addListener(ChannelFutureListener.CLOSE); }
即上面的 echoMsg.retain()
方法.blog
echoMsg
對象, 例如:DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(echoMsg));
即, 使用 Unpooled.copiedBuffer(...)
來複制多一分內存數據~生命週期
ChannelInboundHandlerAdapter
自動手動處理釋放, 以避免像 SimpleChannelInboundHandler
那樣致使屢次釋放引用計數對象~package hello.in; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; public class EchoHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(final ChannelHandlerContext ctx, final Object msg) { if (msg instanceof HttpContent) { manual(ctx, (HttpContent) msg); } } protected void manual(final ChannelHandlerContext ctx, final HttpContent msg) { System.out.println("收到" + msg); ByteBuf echoMsg = msg.content(); System.out.println(new String(ByteBufUtil.getBytes(echoMsg))); DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, echoMsg); response.headers().set("Content-Type", "text/plain"); ctx.write(response).addListener(ChannelFutureListener.CLOSE); } @Override public void channelReadComplete(final ChannelHandlerContext ctx) { ctx.flush(); } @Override public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) { cause.printStackTrace(); ctx.close(); } }