原文地址:http://netty.io/wiki/reference-counted-objects.html

从Netty4开始,某些对象的饿生命周期由其引用计数来管理,因此,一旦不再使用,Netty就可以将它们(或其共享资源)返回给对象池(或对象分配器)。垃圾收集和引用队列并没有提供不可达的高效实时保证,而引用计数则提供了一种可替代的机制,代价是有轻微的不方便。

ByteBuf是最值得注意的一种,它利用了引用计数来提高分配和回收的性能,本节将解释哈在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;

–Dangling引用
尝试访问引用计数为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;
}

–派生的缓冲区
ByteBuf.duplicate(), ByteBuf.slice() and 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() and 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被包含在一个缓冲区中,例如DatagramPacket、HttpContent和WebSocketframe。这些类型继承了一个名为ByteBufHolder的公共接口。
一个buffer holder共享它所包含的缓冲区的引用计数,就像派生的缓冲区一样。

–CHannelHandler中的引用计数
-进站消息
当一个事件循环将数据读入ByteBuf并触发一个ChannelRead()事件时,相应pipline中的ChannelHandle负责释放buffer。因此,处理接收到的数据的handler应该在它的channelRead()中调用buffer的release()。

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

正如本文档中“谁来销毁”一节所描述的,如果处理器将缓冲区(或者任何引用计数对象)传递给下一个处理程序,则不需要释放它。

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

请注意,ByteBuf不是Netty中唯一的引用计数类型。如果你正在处理由解码器生成的消息,则很可能该消息也是引用计数的。

// 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(msg)。

–出站消息
与进站消息不同的是,出站消息是由你的应用程序创建的,将这些消息释放在将其写入到线路后是Netty的责任。但是,拦截你的写入请求的处理器应确保正确释放任何中间对象。(比如编码器)

// Simple-pass through
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实现,因此,即使它们的引用计数不为0,它也会在它们变得不可访问时自动对其进行GC。一旦回收的垃圾无法恢复,也就无法返回它所来自的池,从而产生内存泄漏。
幸运的是,尽管很难找到内存泄漏,但Netty将在默认的情况下抽取大约1%的缓冲区来检查应用中是否存在内存泄漏。如果发生泄漏,你可以找到如下日志信息:

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

使用上面提到的JVM选项重新启动你的应用,然后你将看到你的应用可以访问泄漏的缓冲区的最近位置。下面的例子来自单元测试显示了一个泄漏:

Running io.netty.handler.codec.xml.XmlFrameDecoderTest
15:03:36.886 [main] ERROR io.netty.util.ResourceLeakDetector - LEAK: ByteBuf.release() was not called before it's garbage-collected.
Recent access records: 1
#1:io.netty.buffer.AdvancedLeakAwareByteBuf.toString(AdvancedLeakAwareByteBuf.java:697)io.netty.handler.codec.xml.XmlFrameDecoderTest.testDecodeWithXml(XmlFrameDecoderTest.java:157)io.netty.handler.codec.xml.XmlFrameDecoderTest.testDecodeWithTwoMessages(XmlFrameDecoderTest.java:133)...Created at:io.netty.buffer.UnpooledByteBufAllocator.newDirectBuffer(UnpooledByteBufAllocator.java:55)io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:155)io.netty.buffer.UnpooledUnsafeDirectByteBuf.copy(UnpooledUnsafeDirectByteBuf.java:465)io.netty.buffer.WrappedByteBuf.copy(WrappedByteBuf.java:697)io.netty.buffer.AdvancedLeakAwareByteBuf.copy(AdvancedLeakAwareByteBuf.java:656)io.netty.handler.codec.xml.XmlFrameDecoder.extractFrame(XmlFrameDecoder.java:198)io.netty.handler.codec.xml.XmlFrameDecoder.decode(XmlFrameDecoder.java:174)io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:227)io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:140)io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:74)io.netty.channel.embedded.EmbeddedEventLoop.invokeChannelRead(EmbeddedEventLoop.java:142)io.netty.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:317)io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846)io.netty.channel.embedded.EmbeddedChannel.writeInbound(EmbeddedChannel.java:176)io.netty.handler.codec.xml.XmlFrameDecoderTest.testDecodeWithXml(XmlFrameDecoderTest.java:147)io.netty.handler.codec.xml.XmlFrameDecoderTest.testDecodeWithTwoMessages(XmlFrameDecoderTest.java:133)...

-泄漏检测级别
目前有4种泄漏检测:
DISABLED - 禁用泄漏检测。不推荐
SIMPLE - 取样的1%是否发生了泄漏。默认情况
ADVANCED - 取样的1%发生泄漏的地方
PARANOID - 与ADVANCED类似,但是检查所有的缓冲区,而不只是取样的1%。此选项在自动测试的阶段很有用。如果构建输出包含了LEAK,可以认为构建失败。

你可以使用JVM的Y JVM option -Dio.netty.leakDetection.level来制定泄漏检测级别。
java -Dio.netty.leakDetectionLevel=advanced …

-避免泄漏的最佳实践
在PARANOID和SIMPLE泄漏检测级别运行你的单元测试和集成测试。
在一个足够长的时间内,使用SIMPLE级别推出到整个级别的应用,看是否有泄漏
如果有泄漏,再使用ADVANCED级别来cannary以获得一些关于泄漏的提示。
不要部署存在泄漏的程序到整个集群。

–在单元测试中修复泄漏
在单元测试中,很容易忘记释放缓冲区或者消息。它将生成泄漏警告,但并不意味着你的程序有泄漏。你可以使用ReferenceCountUtil. releaseLater ()方法,而不是使用try-catch块来包装单元测试以释放所有的缓冲区。
import static io.netty.util.ReferenceCountUtil.*;


@Test
public void testSomething() throws Exception {// ReferenceCountUtil.releaseLater() will keep the reference of buf,// and then release it when the test thread is terminated.ByteBuf buf = releaseLater(Unpooled.directBuffer(512));...
}

Reference counted Objects (引用计数对象) - 文章翻译相关推荐

  1. 【Netty4】netty ByteBuf (二) 引用计数对象(reference counted objects)

    原文出处:http://netty.io/wiki/reference-counted-objects.html 相关文章: netty ByteBuf (一)如何创建ByteBuf对象 netty ...

  2. netty 引用计数对象(reference counted objects)

    [Netty官方文档翻译]引用计数对象(reference counted objects) http://damacheng009.iteye.com/blog/2013657 转载于:https: ...

  3. 【Netty官方文档翻译】引用计数对象(reference counted objects)

    原文出处:http://netty.io/wiki/reference-counted-objects.html 原文地址可能有变,且内容可能发生变化. 如果转载请注明出处,谢谢合作^_^. 自从Ne ...

  4. Netty的引用计数对象

    从Netty 4起,对象的生命周期由它们的引用计数来管理,因此,一旦对象不再被引用后,Netty 会将它(或它共享的资源)归还到对象池(或对象分配器).在垃圾回收和引用队列不能保证这么有效.实时的不可 ...

  5. Netty的深入浅出--79.Netty官方Reference Counted Objects文档说明

    netty4之后通过确定的一个the life cycle of certain object 来管理reference counts,一但不在被使用的时候返回一个object pool 简单来说就是 ...

  6. python学习高级篇(part9)--对象的引用计数

    学习笔记,仅供参考,有错必纠 文章目录 python 学习高级篇 类对象的特殊方法之`__str__()` 类对象的特殊方法之`__new__()` 对象的引用计数 什么是引用计数 对象的引用计数加1 ...

  7. c++ reference counting引用计数原理

    我们都知道,c++最令人头疼的问题也是被其他语言鄙视的问题--指针管理.而引用技术能够让上面的简化了不少.下面说说c++引用计数的设计原理. 引用计数的两个动机: 1. 简化heap对象周边的记录工作 ...

  8. Rust for Linux 源码导读 | Ref 引用计数容器 原创

    引子 2022 年,我们很可能会看到 Linux 内核中的实验性 Rust 编程语言支持成为主流.2021.12.6 早上发出了更新的补丁,介绍了在内核中处理 Rust 的初始支持和基础设施. 这次更 ...

  9. 【深入Cocos2d-x】探索Cocos2d-x中的内存管理-引用计数和自动释放池

    2019独角兽企业重金招聘Python工程师标准>>> #深入Cocos2d-x-探索Cocos2d-x中的内存管理-引用计数和自动释放池 ###引用计数(Reference Cou ...

最新文章

  1. SAP MM 启用了Advanced Return Management之后采购订单上的一些变化
  2. 【转载】OSPF网络类型
  3. Arduino处理STM32中的多个串口通讯问题
  4. 各种编程语言功能综合简要介绍(C,C++,JAVA,PHP,PYTHON,易语言)
  5. 数据中心级交换机考核方法
  6. startActivity( ) 与startActivityForResult( )的区别
  7. WinAPI: midiOutCachePatches - 预装音色
  8. 如何修改zblog的css,修改Zblog中的CSS的方法
  9. android Activity的Task Affinity lanchMode
  10. Java编程:获取用户是否输入回车符
  11. 贺利坚老师汇编课程48笔记:转移指令如jcxz
  12. 粒子群课设_GitHub - LIYAJUN2018/tscss: 基于粒子群算法的中职自动排课系统
  13. 设计模式(1):工厂模式
  14. WebClient Exceeded limit on max bytes to buffer : 262144 异常解决
  15. vbscript for 转 php for,VBS教程:VBscript语句-For...Next 语句
  16. HDU 5857 Median(找中位数)
  17. 计算机误删恢复软件,电脑误删文件恢复软件_手机数据恢复工具-万能数据恢复大师...
  18. python生成文字点选验证码→训练yolo目标检测模型→识别文字点选验证码
  19. 从RCNN,Fast-RCNN到Fater-RCNN的演化过程
  20. 无线华为能连苹果不能连接到服务器,华为手机连苹果Mac,连不上?手把手教你...

热门文章

  1. 英雄不问出处--十大名企用人理念
  2. 这些优质的教育类公众号您知道么,非常实用!
  3. 常见的名片尺寸如何在CorelDRAW预设
  4. AIX 上总有一种压缩方式适合你
  5. Java中的switch
  6. 深度linux时间.年日调整,deepin深度商店中的Linux版应用体验分享(一)
  7. 运用fiddler工具深度配置证书抓苹果IOS微信小程序或app数据请求
  8. 如何将音乐转换成 MP3 格式?分享三个方法!
  9. 融合通信无人值守系统解决方案
  10. 分享88个HTML旅游交通模板,总有一款适合您