当前位置: 首页 >服务端 > Netty中常见的IllegalReferenceCountException异常原因及解决

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)) {

@SuppressWaings("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();

}

}

作者:Full Stack Developme
来源链接:https://blog.csdn.net/lizhengyu891231/article/details/88553560

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。





本文链接:https://www.javaclub.cn/server/113147.html

标签:Netty
分享给朋友:

“Netty中常见的IllegalReferenceCountException异常原因及解决” 的相关文章

划分子网和构造超网的学习 2022年05月17日 13:57:19
CentOS yum 源的配置与使用 2022年05月19日 19:54:27
linux yum命令详解 2022年05月19日 19:54:29
Spring Boot+微信小程序 2022年05月20日 21:27:40
枚举法 之Java实现凑硬币 2022年05月21日 11:37:17
中间件RabbitMQ 2022年05月23日 16:45:42
软件工程专业如何就业! 2022年05月23日 22:31:42