在下面代码中
public class EchoServerHandler extends SimpleChannelInboundHandler {
@Override
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.println(
"server receive: " + in.toString(CharsetUtil.UTF_8)
);
//此处回报异常io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
ctx.write(in);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
super.channelReadComplete(ctx);
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
在channelRead0()方法中的ctx.write(in),会报io.netty.util.IllegalReferenceCountException
原因是这是因为Netty有引用计数器的原因,自从Netty 4开始,对象的生命周期由它们的引用计数(reference counts)管理,而不是由垃圾收集器(garbage collector)管理了。
ByteBuf是最值得注意的,它使用了引用计数来改进分配内存和释放内存的性能。在我们创建ByteBuf对象后,它的引用计数是1,当你释放(release)引用计数对象时,它的引用计数减1,如果引用计数为0,这个引用计数对象会被释放(deallocate),并返回对象池。
当尝试访问引用计数为0的引用计数对象会抛出IllegalReferenceCountException异常:
/**
* 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);
}
}
应用新建的Bytebuf返回数据,
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.println(
"server receive: " + in.toString(CharsetUtil.UTF_8)
);
String str = in.toString(CharsetUtil.UTF_8);
/**
*
* ctx.write(in);
* Bytebuf并不适合直接重用,必须新建才能写回客户端,
* 否则将会报io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
* 原因是这是因为Netty4有引用计数器的原因,自从Netty 4开始,对象的生命周期由它们的引用计数(reference counts)管理,
* 而不是由垃圾收集器(garbage collector)管理了。ByteBuf是最值得注意的,它使用了引用计数来改进分配内存和释放内存的性能。
* 在我们创建ByteBuf对象后,它的引用计数是1,当你释放(release)引用计数对象时,它的引用计数减1,
* 如果引用计数为0,这个引用计数对象会被释放(deallocate),并返回对象池。
* 当尝试访问引用计数为0的引用计数对象会抛出IllegalReferenceCountException异常:
* 或者在该Bytebuf 在复用前需要调用retain(),将计数器置为1
*/
ByteBuf pong = Unpooled.copiedBuffer(str.getBytes());
ctx.write(pong);
}
或者在复用前调用retain()方法
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.println(
"server receive: " + in.toString(CharsetUtil.UTF_8)
);
in.retain();
ctx.write(in);
}
或者该方法继承ChannelInboundHandlerAdapter实现其channelRead(ChannelHandlerContext ctx, Object msg)方法并不会报错,原因是并未检验ByteBuf的计数器