netty的异常分析 IllegalReferenceCountException refCnt: 0, decrement: 1

Posted 流子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了netty的异常分析 IllegalReferenceCountException refCnt: 0, decrement: 1相关的知识,希望对你有一定的参考价值。

在websocket关闭时经常会抛出如下异常: IllegalReferenceCountException refCnt: 0, decrement: 1

io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
	at io.netty.buffer.AbstractReferenceCountedByteBuf.release(AbstractReferenceCountedByteBuf.java:101)
	at io.netty.buffer.DefaultByteBufHolder.release(DefaultByteBufHolder.java:73)
	at io.netty.util.ReferenceCountUtil.release(ReferenceCountUtil.java:59)
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:112)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:318)
	at io.netty.channel.AbstractChannelHandlerContext.access$600(AbstractChannelHandlerContext.java:42)
	at io.netty.channel.AbstractChannelHandlerContext$7.run(AbstractChannelHandlerContext.java:309)
	at io.netty.util.concurrent.DefaultEventExecutor.run(DefaultEventExecutor.java:36)
	at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
	at java.lang.Thread.run(Unknown Source)


原来是在服务器收到CloseWebSocketFrame后,SimpleChannelInboundHandler调用release时,会触发CloseWebSocketFrame.release()

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception

  boolean release = true;
  try 
    if (acceptInboundMessage(msg))
    
      Object imsg = msg;
      channelRead0(ctx, imsg);
     else 
      release = false;
      ctx.fireChannelRead(msg);
    
   finally 
    if ((this.autoRelease) && (release))
      ReferenceCountUtil.release(msg);//这行


  

通过逐步debug,发现WebSocketServerHandler报异常的代码在handleWebSocketFrame方法中:

		if (frame instanceof CloseWebSocketFrame) 
			CloseWebSocketFrame close=(CloseWebSocketFrame) frame.retain()//这行
			handshaker.close(ctx.channel(),close);
			return;
		

跟踪发现,它调用了AbstractReferenceCountedByteBuf

public ByteBuf retain()

  while (true) 
    int refCnt = this.refCnt;
    if (refCnt == 0) 
      throw new IllegalReferenceCountException(0, 1)//这行

    
    if (refCnt == 2147483647) 
      throw new IllegalReferenceCountException(2147483647, 1);
    
    if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) 
      break;
    
  
  return this;

在netty4中,对象的生命周期由引用计数器控制,ByteBuf就是如此,每个对象的初始化引用计数为1,调用一次release方法,引用计数器会减1,当尝试访问计数器为0的,对象时,会抛出IllegalReferenceCountException,正如ensureAccessible的实现,更加详细的解释可以参考官方文档

所以需要在对象CloseWebSocketFrame的初始化时引用人为+1

在ProtobufVarint32FrameDecoder中添加如下标红代码:

//CloseWebSocketFrame
 /**
*  Be aware that you need to call @link io.netty.util.ReferenceCounted#retain() on messages that are just passed through if they
* are of type @link io.netty.util.ReferenceCounted. This is needed as the @link MessageToMessageDecoder will call
* @link io.netty.util.ReferenceCounted#release() on decoded messages.
* 具体参考:
*/
ReferenceCountUtil.retain(frame);//这行
out.add(frame);

要读取当前的引用计数值用

ByteBuf.refCnt()

以上是关于netty的异常分析 IllegalReferenceCountException refCnt: 0, decrement: 1的主要内容,如果未能解决你的问题,请参考以下文章

Netty OOM案例

Funning things of netty

Netty 消息接收类故障案例分析

万分之一错误率问题的分析及定位

一个低级错误引发Netty编码解码中文异常

IOS推送http2使用netty服务启动异常