简单写一个示例代码:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(msg.toString());
t = msg;
ctx.write(msg);
ctx.fireChannelRead(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
ctx.flush();
super.channelReadComplete(ctx);
}
这个代码在ctx.fireChannelRead(msg);的时候,最终会调用到tail的inboundhandle。tail inboundhandle是netty默认的最后一个处理
msg的handle。将msg进行realse。那么当 ctx.flush();的时候,msg已经被释放掉,再读取msg,就会报错。
所以如果直接写 ctx.write(msg);复用msg的时候,坚决不能释放msg的引用。那么msg的引用什么时间会被释放呢?
当flush调用成功后,真正写入channel以后,DefaultChannelPipeline$HeadContext,最终会调用ChannelOutboundBuffer的
remove方法,将已经写入的内容从待写的链表中删除。
public boolean remove() {
Entry e = flushedEntry;
if (e == null) {
clearNioBuffers();
return false;
}
Object msg = e.msg;
ChannelPromise promise = e.promise;
int size = e.pendingSize;
removeEntry(e);
if (!e.cancelled) {
// only release message, notify and decrement if it was not canceled before.
ReferenceCountUtil.safeRelease(msg);
safeSuccess(promise);
decrementPendingOutboundBytes(size, false, true);
}
// recycle the entry
e.recycle();
return true;
}
在ReferenceCountUtil.safeRelease(msg);会删除掉msg的引用。释放msg。
总之,在tail的inboundhandle会free掉msg。而head的outboundhandle会在写入channel后,free掉msg。
如果需要在inbound中回写msg。可以参见《netty 实战》的2.3.1的写法。
注意如下写法也会报错:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(msg.toString());
t = msg;
ctx.writeAndFlush(msg);
ctx.fireChannelRead(msg);
}
这是因为ctx.writeAndFlush(msg)执行成功后(真正写入到channel中,因为writeandflush可能尝试16次依然无法写入成功
会放入到队列中下次继续写),msg会被释放。当ctx.firechannelRead到tail handle时,会因为msg的引用已经为0,而报错。