1.前情提要
前面说到的解决方案,由于陆续的改动,还是存在很大的一个问题。会引发内存泄漏,原因是某些bytebuf流没被释放回收。
前情代码:
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> out) throws Exception {
if(in.readableBytes() <= 0){
log.info("***********当前无数据报文,无需解码***********");
return;
}
ByteBuf dup = in.retainedDuplicate();
SocketData data = new SocketData(dup);
data.setpId(this.pId);
ChannelData channelData = new ChannelData(data);
ReferenceCountUtil.retain(in);
out.add(channelData);
in.skipBytes(in.readableBytes());
}
2.两个知识点也是相互产生bug的原因
- 第一点是retainedDuplicate不只是一个简单的复制bytebuf,而是之后,这两个bytebuf对象会共用一个计数;也就是说假设原先in的计数是1,在retainedDuplicate之后,in和dup的计数值都是2。
- 第二点就是当在
ByteBuf
中使用a时ChannelPipeline
,您需要牢记其他规则:管道中的每个入站(aka上游)处理程序都必须释放接收到的消息。Netty不会自动为您释放它们。请注意,编解码器框架确实会自动释放消息,并且如果用户想按原样将消息传递给下一个处理程序,则用户必须增加引用计数。当出站(又名下游)消息到达管道的开头时,Netty将其写出后将其释放。 -- 来源于netty官方文档Netty.docs: New and noteworthy in 4.0 这也是我之前为什么ReferenceCountUtil.retain(in);的原因。 - 问题来了;第一点和第二点碰到一块,那就产生bug了;由于共享计数,相当于多维护增加了一次引用计数。这也就导致了没有释放成功,内存泄漏
3.最终解决方案【追补,我前面居然忘记说!吃惊】
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> out) throws Exception {
if(in.readableBytes() <= 0){
return;
}
ByteBuf dup = in.readBytes(in.readableBytes());
SocketData data = new SocketData(dup);
data.setpId(this.pId);
ChannelData channelData = new ChannelData(data);
out.add(channelData);
}