netty的异常分析 IllegalReferenceCountException refCnt: 0
netty的异常 IllegalReferenceCountException refCnt: 0
这是因为Netty有引用计数器的原因,自从Netty 4开始,对象的生命周期由它们的引用计数(reference counts)管理,而不是由垃圾收集器(garbage collector)管理了。ByteBuf是最值得注意的,它使用了引用计数来改进分配内存和释放内存的性能。
在我们创建ByteBuf对象后,它的引用计数是1,当你释放(release)引用计数对象时,它的引用计数减1,如果引用计数为0,这个引用计数对象会被释放(deallocate),并返回对象池。
当尝试访问引用计数为0的引用计数对象会抛出IllegalReferenceCountException异常:
/**
* Should be called by every method that tries to access the buffers content to check
* if the buffer was released before.
*/
protected final void ensureAccessible() {
if (checkAccessible && refCnt() == 0) {
throw new IllegalReferenceCountException(0);
}
}
这个问题产生的最要原因是在第一次发送完心跳请求后,ctx.write 等一系列方法调用了ByteBuf的release方法,将其引用计数减为了0 导致的:
我们主要看其方法栈中调用信息,得到一个结论,是每次调用ctx.write/writeAndFlush, pipeline.write/writeAndFlush , 等一系列方法时,被封装的ByteBuf对象的引用计数会减一。导致第二次使用同样对象的包装对象时,出现引用计数的问题。
可以调用 echoMsg.refCnt(); 来获取当前引用计数值. 在 ctx.write(...) 前后加一行打印, 就可以发现, ctx.write(...) 完之后, 引用计数减少了1.
解决
- 如果不想创建新的数据, 则可以直接在原对象里调用
echoMsg.retain()进行引用计数加1.例如:
@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对象, 例如:
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(echoMsg));
即, 使用 Unpooled.copiedBuffer(...) 来复制多一份内存数据~
- 直接使用
ChannelInboundHandlerAdapter自动手动处理释放, 以免像SimpleChannelInboundHandler那样导致多次释放引用计数对象~
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();
}
}
netty的异常分析 IllegalReferenceCountException refCnt: 0的更多相关文章
- Netty源码分析第4章(pipeline)---->第6节: 传播异常事件
Netty源码分析第四章: pipeline 第6节: 传播异常事件 讲完了inbound事件和outbound事件的传输流程, 这一小节剖析异常事件的传输流程 首先我们看一个最最简单的异常处理的场景 ...
- [编织消息框架][netty源码分析]11 UnpooledHeapByteBuf 与 ByteBufAllocator
每种ByteBuf都有相应的分配器ByteBufAllocator,类似工厂模式.我们先学习UnpooledHeapByteBuf与其对应的分配器UnpooledByteBufAllocator 如何 ...
- [编织消息框架][netty源码分析]11 ByteBuf 实现类UnpooledHeapByteBuf职责与实现
每种ByteBuf都有相应的分配器ByteBufAllocator,类似工厂模式.我们先学习UnpooledHeapByteBuf与其对应的分配器UnpooledByteBufAllocator 如何 ...
- Netty源码分析第5章(ByteBuf)---->第9节: ByteBuf回收
Netty源码分析第五章: ByteBuf 第九节: ByteBuf回收 之前的章节我们提到过, 堆外内存是不受jvm垃圾回收机制控制的, 所以我们分配一块堆外内存进行ByteBuf操作时, 使用完毕 ...
- netty源码解解析(4.0)-18 ChannelHandler: codec--编解码框架
编解码框架和一些常用的实现位于io.netty.handler.codec包中. 编解码框架包含两部分:Byte流和特定类型数据之间的编解码,也叫序列化和反序列化.不类型数据之间的转换. 下图是编解码 ...
- Netty源码分析之ByteBuf引用计数
引用计数是一种常用的内存管理机制,是指将资源的被引用次数保存起来,当被引用次数变为零时就将其释放的过程.Netty在4.x版本开始使用引用计数机制进行部分对象的管理,其实现思路并不是特别复杂,它主要涉 ...
- netty源码解解析(4.0)-10 ChannelPipleline的默认实现--事件传递及处理
事件触发.传递.处理是DefaultChannelPipleline实现的另一个核心能力.在前面在章节中粗略地讲过了事件的处理流程,本章将会详细地分析其中的所有关键细节.这些关键点包括: 事件触发接口 ...
- Netty源码分析第2章(NioEventLoop)---->第8节: 执行任务队列
Netty源码分析第二章: NioEventLoop 第八节: 执行任务队列 继续回到NioEventLoop的run()方法: protected void run() { for (;;) ...
- Netty源码分析第4章(pipeline)---->第7节: 前章节内容回顾
Netty源码分析第四章: pipeline 第七节: 前章节内容回顾 我们在第一章和第三章中, 遗留了很多有关事件传输的相关逻辑, 这里带大家一一回顾 首先看两个问题: 1.在客户端接入的时候, N ...
随机推荐
- Xcode 编译静态库
有时候,我们需要将一部分经常用到的代码提取出来用来复用,或者说需要用到c++的代码的时候,可以通过编译成静态库的方式来使用.本文中使用的Xcode版本是8.3,静态库制作过程和其他版本基本一样,可能出 ...
- wsgi pep333
转自:https://www.aliyun.com/jiaocheng/444966.html?spm=5176.100033.1.11.559a7C 摘要:wsgi介绍参考:pep333wsgi有两 ...
- 命令行操作flask
Flask-Script 先安装pip3 install Flask-Script from sansa import create_app from flask_script import Mana ...
- DKLang Translation Editor
https://yktoo.com/en/software/dklang-traned Features Translation using a dictionary (so-called Trans ...
- 服务不支持 chkconfig 的解决方法
系统服务,在chkconfig --add servername的时候老是提示服务不支持 chkconfig,经过查找,解决办法如下. 示例,auto_run的前三行如下:#!/bin/sh#chk ...
- 五分钟学会 Kotlin 语法
为什么使用Kotlin 项目一期在收尾了终于有时间折腾了,一个多月以来Kotlin从入门到现在,坚持用来开发的切身感受.因为语法与Java的区别挺大的一开始很想放弃,如果不是因为项目在使用,想必很少人 ...
- Angular学习笔记—路由(转载)
创建路由 1.首先安装 Angular Router.你可以通过运行以下任一操作来执行此操作: yarn add @angular/router # OR npm i --save @angular/ ...
- 用仿ActionScript的语法来编写html5——第二篇,利用Sprite来实现动画
上一篇,我已经模仿as,加入了LBitmap和LBitmapData类,并且用它们实现了静态图片的显示.这次用Sprite来动态显示图片.依然遵循上一篇对显示对象的处理的思路,添加LSprite类,并 ...
- Ubuntu 13.04安装mysql workbench
1.如果你没有更换源,是主服务器的官网源,这时候你可以直接用sudo apt-get install mysql-workbench来安装(前提是已经装好mysql相关服务) 2.如果你第一条装不了, ...
- beego——flash数据
这个flash与 Adobe/Macromedia Flash没有任何关系,它主要用于在两个逻辑间传递临时数据, flash中存放的所有数据会在紧接着的下一个逻辑中调用后清除. 一般用于传递提示和错误 ...