netty系列之:JVM中的Reference count原来netty中也有

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475
目录* 简介

  • ByteBuf和ReferenceCounted
  • ByteBuf的基本使用
  • ByteBuf的回收
  • ByteBuf的衍生方法
  • ChannelHandler中的引用计数
  • 内存泄露
  • 总结

简介

为什么世界上有这么多JAVA的程序员呢?其中一个很重要的原因就是JAVA相对于C++而言,不需要考虑对象的释放,一切都是由垃圾回收器来完成的。在崇尚简单的现代编程世界中,会C++的高手越来越少,会JAVA的程序员越来越多。

JVM的垃圾回收器中一个很重要的概念就是Reference count,也就是对象的引用计数,用来控制对象是否还被引用,是否可以被垃圾回收。

netty也是运行在JVM中的,所以JVM中的对象引用计数也适用于netty中的对象。这里我们说的对象引用指的是netty中特定的某些对象,通过对象的引用计数来判断这些对象是否还被使用,如果不再被使用的话就可以把它们(或它们的共享资源)返回到对象池(或对象分配器)。

这就叫做netty的对象引用计数技术,其中一个最关键的对象就是ByteBuf。

ByteBuf和ReferenceCounted

netty中的对象引用计数是从4.X版本开始的,ByteBuf是其中最终要的一个应用,它利用引用计数来提高分配和释放性能.

先来看一下ByteBuf的定义:

public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf>

可以看到ByteBuf是一个抽象类,它实现了ReferenceCounted的接口。

ReferenceCounted就是netty中对象引用的基础,它定义了下面几个非常重要的方法,如下所示:

int refCnt();ReferenceCounted retain();ReferenceCounted retain(int increment);boolean release();boolean release(int decrement);

其中refCnt返回的是当前引用个数,retain用来增加引用,而release用来释放引用。

ByteBuf的基本使用

刚分配情况下ByteBuf的引用个数是1:

ByteBuf buf = ctx.alloc().directBuffer();
assert buf.refCnt() == 1;

当调用他的release方法之后,refCnt就变成了0:

boolean destroyed = buf.release();
assert destroyed;
assert buf.refCnt() == 0;

当调用它的retain方法,refCnt就会加一:

ByteBuf buf = ctx.alloc().directBuffer();
assert buf.refCnt() == 1;
buf.retain();
assert buf.refCnt() == 2;

要注意的是,如果ByteBuf的refCnt已经是0了,就表示这个ByteBuf准备被回收了,如果再调用其retain方法,则会抛出IllegalReferenceCountException:refCnt: 0, increment: 1

所以我们必须在ByteBuf还未被回收之前调用retain方法。

既然refCnt=0的情况下,不能调用retain()方法,那么其他的方法能够调用吗?

我们来尝试调用一下writeByte方法:

        try {buf.writeByte(10);} catch (IllegalReferenceCountException e) {log.error(e.getMessage(),e);}

可以看到,如果refCnt=0的时候,调用它的writeByte方法会抛出IllegalReferenceCountException异常。

这样看来,只要refCnt=0,说明这个对象已经被回收了,不能够再使用了。

ByteBuf的回收

既然ByteBuf中保存的有refCnt,那么谁来负责ByteBuf的回收呢?

netty的原则是谁消费ByteBuf,谁就负责ByteBuf的回收工作。

在实际的工作中,ByteBuf会在channel中进行传输,根据谁消费谁负责销毁的原则,接收ByteBuf的一方,如果消费了ByteBuf,则需要将其回收。

这里的回收指的是调用ByteBuf的release()方法。

ByteBuf的衍生方法

ByteBuf可以从一个parent buff中衍生出很多子buff。这些子buff并没有自己的reference count,它们的引用计数是和parent buff共享的,这些提供衍生buff的方法有:ByteBuf.duplicate(), ByteBuf.slice() 和 ByteBuf.order(ByteOrder)。

buf = directBuffer();ByteBuf derived = buf.duplicate();assert buf.refCnt() == 1;assert derived.refCnt() == 1;

因为衍生的byteBuf和parent buff共享引用计数,所以如果要将衍生的byteBuf传给其他的流程进行处理的话,需要调用retain()方法:

ByteBuf parent = ctx.alloc().directBuffer(512);
parent.writeBytes(...);try {while (parent.isReadable(16)) {ByteBuf derived = parent.readSlice(16);derived.retain();process(derived);}
} finally {parent.release();
}
...public void process(ByteBuf buf) {...buf.release();
}

ChannelHandler中的引用计数

netty根据是读消息还是写消息,可以分为InboundChannelHandler和OutboundChannelHandler,分别用来读消息和写消息。

根据谁消费,谁释放的原则,对Inbound消息来说,读取完毕之后,需要调用ByteBuf的release方法:

public void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf buf = (ByteBuf) msg;try {...} finally {buf.release();}
}

但是如果你只是将byteBuf重发到channel中供其他的步骤进行处理,则不需要release:

public void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf buf = (ByteBuf) msg;...ctx.fireChannelRead(buf);
}

同样的在Outbound中,如果只是简单的重发,则不需要release:

public void write(ChannelHandlerContext ctx, Object message, ChannelPromise promise) {System.err.println("Writing: " + message);ctx.write(message, promise);
}

如果是处理了消息,则需要release:

public void write(ChannelHandlerContext ctx, Object message, ChannelPromise promise) {if (message instanceof HttpContent) {// Transform HttpContent to ByteBuf.HttpContent content = (HttpContent) message;try {ByteBuf transformed = ctx.alloc().buffer();....ctx.write(transformed, promise);} finally {content.release();}} else {// Pass non-HttpContent through.ctx.write(message, promise);}
}

内存泄露

因为reference count是netty自身来进行维护的,需要在程序中手动进行release,这样会带来一个问题就是内存泄露。因为所有的reference都是由程序自己来控制的,而不是由JVM来控制,所以可能因为程序员个人的原因导致某些对象reference count无法清零。

为了解决这个问题,默认情况下,netty会选择1%的buffer allocations样本来检测他们是否存在内存泄露的情况.

如果发生泄露,则会得到下面的日志:

LEAK: ByteBuf.release() was not called before it's garbage-collected. Enable advanced leak reporting to find out where the leak occurred. To enable advanced leak reporting, specify the JVM option '-Dio.netty.leakDetectionLevel=advanced' or call ResourceLeakDetector.setLevel()

上面提到了一个检测内存泄露的level,netty提供了4种level,分别是:

  • DISABLED—禁用泄露检测
  • SIMPLE --默认的检测方式,占用1% 的buff。
  • ADVANCED - 也是1%的buff进行检测,不过这个选项会展示更多的泄露信息。
  • PARANOID - 检测所有的buff。

具体的检测选项如下:

java -Dio.netty.leakDetection.level=advanced ...

总结

掌握了netty中的引用计数,就掌握了netty的财富密码!

本文的例子可以参考:learn-netty4

本文已收录于 http://www.flydean.com/43-netty-reference-cound/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401320.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

hdu区域赛在线热身赛 暨 第十二场组队赛

题目编号&#xff1a;hdu 4257~4266 (对应比赛题号1001~1010) 这是我们第十二场组队赛&#xff0c;在今天中午进行。 比赛刚开始&#xff0c;依然是由我的队友读题。还没看几题&#xff0c;就发现了好多题judge时长高达20秒&#xff0c;这真的有点给我们心理造成压力。不过&…

powerdesign相关

1.安装程序和汉化放百度云了 2.打印错误处理 http://jingyan.baidu.com/article/c45ad29cd84e4b051753e2c3.html 3.导出sql http://jingyan.baidu.com/article/7082dc1c48960ee40a89bd38.html 4.name和comment同步 http://blog.csdn.net/steveguoshao/article/details/16940347…

游戏名词

BUFF,DEBUFF: 增益状态&#xff0c;包括自己或者队友施加的&#xff0c;例如骑士的祝福&#xff0c;牧师的耐力精神&#xff0c;小德的爪子DEBUFF就是减益状态&#xff0c;例如你PK的时候法师的寒冰箭减速&#xff0c;盗贼的毒药&#xff0c;SS的腐蚀等等NPC&#xff1a; NPC就…

C语言编程规范--代码注释

目录 1、什么是Doxygen?. 3 2、撰写正确格式的批注... 4 2.1常用指令介绍... 4 2.2简述与详述的方式... 6 2.3文件头注释... 6 2.4版权注释... 6 2.5模块定义&#xff08;单独显示一页&#xff09;... 7 2.6分组定义&#xff08;在一页内分组显示&#xff09;... 8 2.7变量、宏…

Spring系列15:Environment抽象

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 本文内容 Environment抽象的2个重要概念Profile 的使用PropertySource 的使用 Environment抽象的2个重要概念 Environme…

U-Mail邮件服务系统任意文件上传+执行漏洞(runtime缺陷与验证绕过)

http://www.wooyun.org/bugs/wooyun-2010-061859转载于:https://www.cnblogs.com/hookjoy/p/4068326.html

Source Insight使用技巧

一、Source Insight实用技巧&#xff1a; Source Insight(下文的SI指的也是它)就是这样的一个东西&#xff1a;   Windows下开发人员的至爱&#xff0c;功能强大&#xff0c;界面友好。支持语法高亮、符号跳转&#xff0c;还支持函数调用关系图显示。这是一个专业的编程环境&…

剑指offer-翻转单词顺序列

剑指offer-翻转单词顺序列 题目描述 牛客最近来了一个新员工Fish&#xff0c;每天早晨总是会拿着一本英文杂志&#xff0c;写些句子在本子上。同事Cat对Fish写的内容颇感兴趣&#xff0c;有一天他向Fish借来翻看&#xff0c;但却读不懂它的意思。例如&#xff0c;“student. a …

私有化轻量级持续集成部署方案--05-持续部署服务-Drone(上)

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 提示&#xff1a;本系列笔记全部存在于 Github&#xff0c; 可以直接在 Github 查看全部笔记 持续部署概述 持续部署是能…

PS图像菜单下计算命令

PS图像菜单下计算命令通过通道的混合模式得到的选区非常精细&#xff0c;从而调色的时候过度非常好。功能十分强大。 下面用计算命令中的"相加"和"减去"模式做实例解析&#xff0c;这里通道混合模式和图层混合模式原理是一样的。 原图&#xff1a; 实例目…

LINQ系列:LINQ to XML操作

LINQ to XML操作XML文件的方法&#xff0c;如创建XML文件、添加新的元素到XML文件中、修改XML文件中的元素、删除XML文件中的元素等。 1. 创建XML文件 string xmlFilePath Server.MapPath("Data/Product.xml");XDocument doc new XDocument (new XDeclaration(&quo…

C语言编程规范

C语言编程规范 范 围: 本规范适用于公司内使用C语言编码的所有软件。本规范自发布之日起生效&#xff0c;以后新编写的和修改的 代码应遵守本规范。 简 介&#xff1a; 本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效 率、可移…

Ubuntu开发之旅一---安装初步

由于有一台小黑&#xff0c;老机器了&#xff0c;闲置时间不长不短&#xff0c;偶尔拿来用下&#xff0c;总感觉windows跑起来太费力&#xff0c;鉴于有过一段时间的Linux开发经验&#xff08;大概四个月左右&#xff09;&#xff0c;故抽空安装了一个ubuntu&#xff0c;原因有…

win10 VScode配置GCC(MinGW)

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 前提 安装 Visual Studio Code安装 C/C 扩展 for VS Code 也可以在vscode的extension界面搜索’c’查找插件安装 3. 获取最…

复制构造函数的用法及出现迷途指针问题

复制构造函数利用下面这行语句来复制一个对象&#xff1a; A (A &a) 从上面这句话可以看出&#xff0c;所有的复制构造函数均只有一个参数&#xff0c;及对同一个类的对象的引用 比如说我们有一个类A&#xff0c;定义如下&#xff1a; ?12345678910class A{public:A(int i…

Linux下压缩某个文件夹(文件夹打包)

为什么80%的码农都做不了架构师&#xff1f;>>> tar -zcvf /home/xahot.tar.gz /xahot tar -zcvf 打包后生成的文件名全路径 要打包的目录 例子&#xff1a;把/xahot文件夹打包后生成一个/home/xahot.tar.gz的文件。 zip 压缩方法&#xff1a; 压缩当前的文件夹 zi…

解决Warning: Cannot modify header information - headers already sent b...

解决Warning: require(E:\testwwwroot\cc06\wp-admin/wp-includes/compat.php) [function.require]: failed to open stream: No such file or directory in E:\testwwwroot\cc06\wp-admin\wp-settings.php on line 246Fatal error: require() [function.require]: Failed open…

GoJS 使用笔记

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 作为商业软件&#xff0c;GoJs很容易使用&#xff0c;文档也很完备&#xff0c;不过项目中没有时间系统地按照文档学习&…

do while的使用

while循环&#xff1a;while(条件){循环体;} do while循环&#xff1a;do{循环体;}while(条件); //注意do while 有分号 while循环和do while循环只有一个差别&#xff0c;就是&#xff1a;while循环先判断条件&#xff0c;成立才做循环体&#xff1b;do while循环则是先做循环…

Android学习笔记:TabHost 和 FragmentTabHost

2019独角兽企业重金招聘Python工程师标准>>> Android学习笔记&#xff1a;TabHost 和 FragmentTabHostTabHost命名空间&#xff1a;android.widget.TabHost初始化函数&#xff08;必须在addTab之前调用&#xff09;&#xff1a;setup(); 包含两个子元素&#xff1a;…