Netty的引用计数对象
从Netty 4起,对象的生命周期由它们的引用计数来管理,因此,一旦对象不再被引用后,Netty 会将它(或它共享的资源)归还到对象池(或对象分配器)。在垃圾回收和引用队列不能保证这么有效、实时的不可达性检测的情况下,引用计数以牺牲轻微的便利性为代价,提供了另一种可选的解决方案。 最值得注意的类型是ByteBuf,它正是利用了引用计数来提升内存分配和释放的性能
。
一、引用计数基本原理
一个新创建的引用计数对象的初始引用计数是1。
ByteBuf buf = ctx.alloc().directbuffer();
assert buf.refCnt() == 1;
当你释放掉引用计数对象,它的引用次数减1.如果一个对象的引用计数到达0,该对象就会被 释放或者归还到创建它的对象池。
assert buf.refCnt() == 1;
// release() returns true only if the reference count becomes 0.
boolean destroyed = buf.release();
assert destroyed;
assert buf.refCnt() == 0;
- 悬挂引用
访问引用计数为0的引用计数对象会触发一次IllegalReferenceCountException
:
assert buf.refCnt() == 0;
try {buf.writeLong(0xdeadbeef);
throw new Error("should not reach here");
} catch (IllegalReferenceCountExeception e) {// Expected
}
- 增加引用计数
只要引用计数对象未被销毁,就可以通过调用retain()方法来增加引用次数
:
ByteBuf buf = ctx.alloc().directBuffer();
assert buf.refCnt() == 1;
buf.retain();
assert buf.refCnt() == 2;
boolean destroyed = buf.release();
assert !destroyed;
assert buf.refCnt() == 1;
- 谁来销毁
一般的原则是,最后访问引用计数对象的部分负责对象的销毁。更具体地来说:
- 如果一个[发送]组件要传递一个引用计数对象到另一个[接收]组件,发送组件通常不需要 负责去销毁对象,而是将这个销毁的任务推延到接收组件
- 如果一个组件消费了一个引用计数对象,并且不知道谁会再访问它(例如,不会再将引用 发送到另一个组件),那么,这个组件负责销毁工作
这里有一个简单的示例:
public ByteBuf a(ByteBuf input) {input.writeByte(42);return input;
}
public ByteBuf b(ByteBuf input) {try {output = input.alloc().directBuffer(input.readableBytes() + 1);output.writeBytes(input);output.writeByte(42);return output;} finally {input.release();}
}
public void c(ByteBuf input) {System.out.println(input);input.release();
}
public void main() {...ByteBuf buf = ...;// This will print buf to System.out and destroy it.c(b(a(buf)));assert buf.refCnt() == 0;
}
二、子缓冲区(Derived buffers)
调用ByteBuf.duplicate(),ByteBuf.slice()和ByteBuf.order(ByteOrder)三个方法, 会创建一个子缓冲区,子缓冲区共享父缓冲区的内存区域。子缓冲区没有自己的引用计数,而是共享父缓冲区的引用计数。
ByteBuf parent = ctx.alloc().directBuffer();
ByteBuf derived = parent.duplicate();
// Creating a derived buffer does not increase the reference count.
assert parent.refCnt() == 1;
assert derived.refCnt() == 1;
但是,调用ByteBuf.copy()和ByteBuf.readBytes(int)创建的并不是子缓冲区,返回的 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();
}
ByteBufHolder接口
有时候,ByteBuf被缓冲区容器(buffer holder)持有,像DatagramPacket、HttpComponent和WebSocketFrame。 这些类型都继承自一个通用接口,叫做ByteBufHolder。 缓冲区容器(buffer holder)共享它持有的缓冲区的引用计数,和子缓冲区一样。Channel-handler中的引用计数
- 入口消息
当一个事件循环(event loop)读取数据并写入到ByteBuf,在触发一次channelRead()事件后,应该由对应pipeline的 ChannelHandler负责去释放缓冲区的内存。因此,消费接收数据的handler应该在它channelRead()方法中调用数据的 release()方法。
public void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf buf = (ByteBuf) msg;try {...} finally {buf.release();}}
在“谁负责销毁”一节中我们提到,如果你的handler将缓冲区(或者其他任何引用计数对象)传递到下一个handler, 那么你不需要负责去释放。
public void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf buf = (ByteBuf) msg;...ctx.fireChannelRead(buf);
}
需要注意的是,ByteBuf并不是Netty中唯一的引用计数类型。如果你在与解码程序(decoder)生成的消息打交道,这些消息一样可能 是引用计数的。
/ Assuming your handler is placed next to `HttpRequestDecoder`
public void channelRead(ChannelHandlerContext ctx, Object msg) {if (msg instanceof HttpRequest) {HttpRequest req = (HttpRequest) msg;...}if (msg instanceof HttpContent) {HttpContent content = (HttpContent) msg;try {...} finally {content.release();}}
}
如果你有疑虑,或者你想简化释放消息内存的过程,你可以使用ReferenceCountUtil.release():
public void channelRead(ChannelHandlerContext ctx, Object msg) {try {...} finally {ReferenceCountUtil.release(msg);}
}
同样地,你可以考虑继承SimpleChannelHandler,它会帮你调用ReferenceCountUtil.release()释放所有 你接收到的消息内存。
- 出口消息
与入口消息不同的是,出口消息是在你的应用中创建的,由Netty负责在将消息发送出去后释放掉。但是,如果你 有拦截写请求的handler程序,则需要保证正确释放中间对象(例如,编码程序)。
public void write(ChannelHandlerContext ctx, Object message, ChannelPromise promise) {System.err.println("Writing: " + message);ctx.write(message, promise);
}
// Transformation
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);}
}
三、内存泄漏
引用计数的缺点是,引用计数对象容易发生泄露。因为JVM并不知道Netty的引用计数实现,当引用计数对象不可达时,JVM就会将它们GC掉,即时此时它们的引用计数并不为0。一旦对象被GC就不能再访问,也就不能 归还到缓冲池,所以会导致内存泄露。
庆幸的是,尽管发现内存泄露很难,但是Netty会对分配的缓冲区的1%进行采样,来检查你的应用中是否存在内存 泄露。
文章转自
Netty的引用计数对象相关推荐
- 【Netty4】netty ByteBuf (二) 引用计数对象(reference counted objects)
原文出处:http://netty.io/wiki/reference-counted-objects.html 相关文章: netty ByteBuf (一)如何创建ByteBuf对象 netty ...
- 【Netty官方文档翻译】引用计数对象(reference counted objects)
原文出处:http://netty.io/wiki/reference-counted-objects.html 原文地址可能有变,且内容可能发生变化. 如果转载请注明出处,谢谢合作^_^. 自从Ne ...
- netty 引用计数对象(reference counted objects)
[Netty官方文档翻译]引用计数对象(reference counted objects) http://damacheng009.iteye.com/blog/2013657 转载于:https: ...
- Reference counted Objects (引用计数对象) - 文章翻译
原文地址:http://netty.io/wiki/reference-counted-objects.html 从Netty4开始,某些对象的饿生命周期由其引用计数来管理,因此,一旦不再使用,Net ...
- python学习高级篇(part9)--对象的引用计数
学习笔记,仅供参考,有错必纠 文章目录 python 学习高级篇 类对象的特殊方法之`__str__()` 类对象的特殊方法之`__new__()` 对象的引用计数 什么是引用计数 对象的引用计数加1 ...
- 提高C++性能的编程技术笔记:引用计数+测试代码
引用计数(reference counting):基本思想是将销毁对象的职责从客户端代码转移到对象本身.对象跟踪记录自身当前被引用的数目,在引用计数达到零时自行销毁.换句话说,对象不再被使用时自行销毁 ...
- wxWidgets:引用计数
wxWidgets:引用计数 wxWidgets:引用计数 对象比较 对象销毁 引用计数类列表 制作你自己的引用计数类 wxWidgets:引用计数 许多 wxWidgets 对象使用一种称为引用计数 ...
- c++ reference counting引用计数原理
我们都知道,c++最令人头疼的问题也是被其他语言鄙视的问题--指针管理.而引用技术能够让上面的简化了不少.下面说说c++引用计数的设计原理. 引用计数的两个动机: 1. 简化heap对象周边的记录工作 ...
- Rust for Linux 源码导读 | Ref 引用计数容器 原创
引子 2022 年,我们很可能会看到 Linux 内核中的实验性 Rust 编程语言支持成为主流.2021.12.6 早上发出了更新的补丁,介绍了在内核中处理 Rust 的初始支持和基础设施. 这次更 ...
最新文章
- 计算机电缆djyvp工艺,计算机电缆dJyvP相关办法.pdf
- C++的this指针和引用符号的搭配使用理解
- Citrix Xendesktop中VDA注册DDC的流程
- 数据库事务原理详解-事务基本概念
- 《大数据,小时代,向移动互联网迁徙-2012上半年移动互联网数据分享》_DCCI
- 镜像镜像–使用反射在运行时查看JVM内部
- mvc5控制器修改html,关于jquery:如何通过对控制器的ajax调用在MVC5中呈现局部视图并返回HTML...
- 数据科学 IPython 笔记本 9.7 数组上的计算:广播
- 【投放算法】“喵糖”背后的商业化流量投放算法应用及实践
- 《linux核心应用命令速查》连载十二:top:显示进程
- vbs代码弹计算机,如何恶搞朋友的电脑?超简单的vbs代码
- 于歆杰pdf 电路原理_电路原理于歆杰答案pdf
- 3.3.3 使用集线器的星形拓补
- mysql通过Navcat 备份数据.psc 还原数据时 只有表没有数据解决方法
- 短视频SDK接入(2)---环境搭建
- 2022年装饰行业市场分析
- 微信SDK中含有的支付功能怎么去掉?
- C语言之负数的左移/右移运算
- css的几种布局方式都在这
- 学习vue笔记(5)