Netty中常见的IllegalReferenceCountException异常原因及解决

转载自:https://emacsist.github.io/2018/04/28/netty%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%9A%84illegalreferencecountexception%E5%BC%82%E5%B8%B8%E5%8E%9F%E5%9B%A0%E5%8F%8A%E8%A7%A3%E5%86%B3/

 

Netty中常见的IllegalReferenceCountException异常原因及解决

 

问题代码


 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34


 

package hello.in;

import io.netty.buffer.ByteBuf;

import io.netty.buffer.ByteBufUtil;

import io.netty.channel.ChannelHandlerContext;

import io.netty.channel.SimpleChannelInboundHandler;

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 SimpleChannelInboundHandler<HttpContent> {

@Override

protected void channelRead0(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();

}

}

上面的代码原目的是回显的, 但请求一下, 就会报:


 

1

2

3

4

5

6


 

io.netty.util.IllegalReferenceCountException: refCnt: 0, increment: 1

at io.netty.buffer.AbstractReferenceCountedByteBuf.release0(AbstractReferenceCountedByteBuf.java:100)

at io.netty.buffer.AbstractReferenceCountedByteBuf.release(AbstractReferenceCountedByteBuf.java:84)

at io.netty.handler.codec.http.DefaultHttpContent.release(DefaultHttpContent.java:94)

at io.netty.util.ReferenceCountUtil.release(ReferenceCountUtil.java:88)

...

原因

首先要重点强调的是: SimpleChannelInboundHandler 它会自动进行一次释放(即引用计数减1). 源码如下:


 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18


 

@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);

} else {

release = false;

ctx.fireChannelRead(msg);

}

} finally {

if (autoRelease && release) {

ReferenceCountUtil.release(msg);

}

}

}

我们继承这个类, 一般是要重写 channelRead0() 这个方法的, 但实际上 Netty 内部处理的是 channelRead() 方法, 只是它通过模板模式来进行调用而已.

然后, 我们的代码里 DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, echoMsg); 这里使用了原 msg.content()ByteBuf, 然后调用 ctx.write(response) 后, 就会导致 msg 的引用计数减1了.(这时, 引用计数变成0了~)

可以调用 echoMsg.refCnt(); 来获取当前引用计数值. 在 ctx.write(...) 前后加一行打印, 就可以发现, ctx.write(...) 完之后, 引用计数减少了1.

然后最后 channelRead0() 执行完毕返回了, SimpleChannelInboundHandler 的模板方法还会再一次进行释放 release, 这时就会触发 IllegalReferenceCountException 异常了.(参考 [翻译]Netty中的引用计数对象).

解决

  • 如果不想创建新的数据, 则可以直接在原对象里调用 echoMsg.retain() 进行引用计数加1.例如:

 

1

2

3

4

5

6

7

8

9

10


 

@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() 方法.

  • 构造 response 对象时, 不要复用 echoMsg 对象, 例如:

 

1


 

DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(echoMsg));

即, 使用 Unpooled.copiedBuffer(...) 来复制多一份内存数据~

  • 直接使用 ChannelInboundHandlerAdapter 自动手动处理释放, 以免像 SimpleChannelInboundHandler 那样导致多次释放引用计数对象~

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43


 

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();

}

}

发布了27 篇原创文章 · 获赞 32 · 访问量 10万+
展开阅读全文

Netty 遇到的 refCnt: 0异常

09-18

代码运行到ByteBuf in = (ByteBuf) msg; byte[] req = new byte[in.readableBytes()]; in.readBytes(req); 就会报错,哪位大神能解决? public class ZhiNengKaiGuanServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // TODO Auto-generated method stub super.channelActive(ctx); //与服务端建立连接后 System.out.println("链接成功"); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // TODO Auto-generated method stub super.channelRead(ctx, msg); System.out.println("server channelRead.."); System.out.println("recive data:"+msg); ByteBuf in = (ByteBuf) msg; //in.retain(); byte[] req = new byte[in.readableBytes()]; in.readBytes(req); //String resultStr = new String(req); //System.out.println("Client said:" + resultStr); // 释放资源,这行很关键 //in.release(); //String str=new String(req,"UTF-8"); System.out.print("Receive data: { "); for (int i = 0; i <req.length ; i++) { String dhs = "00" + Integer.toHexString(req[i] & 0xFF); System.out.print(dhs.substring(dhs.length() - 2, dhs.length()) .toUpperCase() + " "); } System.out.println(" } "); String ser="hellow ,I'm Server"; ctx.write(ser); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { // TODO Auto-generated method stub super.channelReadComplete(ctx); System.out.println("server channelReadComplete.."); ctx.flush();//刷新后才将数据发出到SocketChannel } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // TODO Auto-generated method stub super.exceptionCaught(ctx, cause); System.out.println("server exceptionCaught.."); cause.printStackTrace(); ctx.close(); } public class ZhiNengKaiGuanServer { private int port; public ZhiNengKaiGuanServer(int port) { this.port = port; } public void run() throws Exception { // 服务器线程组 用于网络事件的处理 一个用于服务器接收客户端的连接 // 另一个线程组用于处理SocketChannel的网络读写 EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) EventLoopGroup workerGroup = new NioEventLoopGroup(); try { // NIO服务器端的辅助启动类 降低服务器开发难度 ServerBootstrap b = new ServerBootstrap(); // (2) b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // (3) 类似NIO中serverSocketChannel .childHandler(new ChannelInitializer<SocketChannel>() { // (4) @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast("encoder", new ObjectEncoder()); //ch.pipeline().addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null))); //ch.pipeline().addLast("decoder", new StringEncoder()); //ch.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); //ch.pipeline().addLast("frameEncoder", new LengthFieldPrepender(4)); // 自己的逻辑Handler ch.pipeline().addLast(new ZhiNengKaiGuanServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 1024) // (5) .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)// 最后绑定I/O事件的处理类 // 服务器绑定端口监听 ChannelFuture f = b.bind(port).sync(); // (7) // 监听服务器关闭监听 f.channel().closeFuture().sync(); } finally { // 优雅退出 释放线程池资源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); System.out.println("服务器优雅的释放了线程资源..."); } } public static void main(String[] args) throws Exception { int port = 9000; if (args != null && args.length > 0) { try { port = Integer.valueOf(args[0]); } catch (NumberFormatException e) { } } new ZhiNengKaiGuanServer(9527).run(); } } public class ZhiNengKaiGuanCommuccationConnector { // 配置客户端NIO线程组 EventLoopGroup group = new NioEventLoopGroup(); private ZhiNengKaiGuan zhiNengKaiGuan; public ZhiNengKaiGuanCommuccationConnector(ZhiNengKaiGuan zhiNengKaiGuan){ this.zhiNengKaiGuan=zhiNengKaiGuan; } public ZhiNengKaiGuan getZhiNengKaiGuan() { return zhiNengKaiGuan; } public void setZhiNengKaiGuan(ZhiNengKaiGuan zhiNengKaiGuan) { this.zhiNengKaiGuan = zhiNengKaiGuan; } public void connect(final byte[] req) throws Exception { int port=zhiNengKaiGuan.getPort(); String IP=zhiNengKaiGuan.getIp(); System.out.println("this zhinengchazuo ip :" + this.zhiNengKaiGuan.getIp() + " port:" + this.zhiNengKaiGuan.getPort()); System.out.print("Send Command: { "); for (int i = 0; i < req.length; i++) { String dhs = "00" + Integer.toHexString(req[i] & 0xFF); System.out.print(dhs.substring(dhs.length() - 2, dhs.length()).toUpperCase() + " "); } System.out.println(" } "); //配置客户端NIO线程组 EventLoopGroup group = new NioEventLoopGroup(); try { //客户端辅助启动类 对客户端配置 Bootstrap b = new Bootstrap(); // (1) b.group(group); // (2) b.channel(NioSocketChannel.class); // (3) b.option(ChannelOption.SO_KEEPALIVE, true); // (4) b.handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast("encoder", new ObjectEncoder()); ch.pipeline().addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null))); ch.pipeline().addLast(new ZhiNengKaiGuanCommuncationHander(req)); } }); //异步链接服务器 同步等待链接成功 ChannelFuture f = b.connect(IP, port).sync(); //等待链接关闭 f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); System.out.println("客户端优雅的释放了线程资源..."); } } } public class ZhiNengKaiGuanCommuncationHander extends ChannelInboundHandlerAdapter { private static final Logger logger=Logger.getLogger(ZhiNengKaiGuanCommuncationHander.class.getName()); private ByteBuf sendMessage; public ZhiNengKaiGuanCommuncationHander(byte[] reqs){ sendMessage=Unpooled.buffer(reqs.length); sendMessage.writeBytes(reqs); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println(sendMessage); ctx.writeAndFlush(sendMessage); System.out.println("客户端active"); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("客户端收到服务器响应数据"); /*ByteBuf m = (ByteBuf) msg; // (1) try { long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L; System.out.println(new Date(currentTimeMillis)); byte[] req=new byte[m.readableBytes()]; CommonUtil.remsg=req; m.readBytes(req); String body=new String(req,"UTF-8"); System.out.println("Now is:"+body); ctx.close(); } finally { m.release(); }*/ } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); System.out.println("客户端收到服务器响应数据处理完成"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.warning("Unexpected exception from downstream:"+cause.getMessage()); ctx.close(); System.out.println("客户端异常退出"); } } public class Test2 { public static void main(String[] args) { ZhiNengKaiGuan s=new ZhiNengKaiGuan("127.0.1.1", 9527); KaiGuanCmd j=new KaiGuanCmd(s); j.query(); } } 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览