引用计数
netty中使用引用计数机制来管理资源,当一个实现ReferenceCounted的对象实例化时,引用计数置1.
客户代码中需要保持一个该对象的引用时需要调用接口的retain方法将计数增1.对象使用完毕时调用release将计数减1.
当引用计数变为0时,对象将释放所持有的底层资源或将资源返回资源池.
内存泄露
按上述规则使用Direct和Pooled的ByteBuf尤其重要.对于DirectBuf,其内存不受VM垃圾回收控制只有在调用release导致计数为0时才会主动释放内存,而PooledByteBuf只有在release后才能被回收到池中以循环利用.
如果客户代码没有按引用计数规则使用这两种对象,将会导致内存泄露.
内存使用跟踪
在netty.io.util包中含有如下两个类
ResourceLeak 用于跟踪内存泄露
ResourceLeakDetector 内存泄露检测工具
在io.netty.buffer.AbstractByteBufAllocator类中有如下代码
//装饰器模式,用SimpleLeakAwareByteBuf或AdvancedLeakAwareByteBuf来包装原始的ByteBuf //两个包装类均通过调用ResourceLeak的record方法来记录ByteBuf的方法调用堆栈,区别在于后者比前者记录更多的内容 protected static ByteBuf toLeakAwareBuffer(ByteBuf buf) { ResourceLeak leak; //根据设置的Level来选择使用何种包装器 switch (ResourceLeakDetector.getLevel()) { case SIMPLE: //重建用于跟踪和表示内容泄露的ResourcLeak对象 leak = AbstractByteBuf.leakDetector.open(buf); if (leak != null) { //只在ByteBuf.order方法中调用ResourceLeak.record buf = new SimpleLeakAwareByteBuf(buf, leak); } break; case ADVANCED: case PARANOID: leak = AbstractByteBuf.leakDetector.open(buf); if (leak != null) { //在ByteBuf几乎所有方法中调用ResourceLeak.record buf = new AdvancedLeakAwareByteBuf(buf, leak); } break; } return buf; }
下图展示了该方法被调用的时机.可见Netty只对PooledByteBuf和DirectByteBuf监控内存泄露.
内存泄露检测
下面观察上述代码中的AbstractByteBuf.leakDetector.open(buf);
实现代码如下
//创建用于跟踪和表示内容泄露的ResourcLeak对象 public ResourceLeak open(T obj) { Level level = ResourceLeakDetector.level; if (level == Level.DISABLED) {//禁用内存跟踪 return null; } if (level.ordinal() < Level.PARANOID.ordinal()) { //如果监控级别低于PARANOID,则只对要监控的资源进行采样 if (leakCheckCnt ++ % samplingInterval == 0) { reportLeak(level); return new DefaultResourceLeak(obj); } else { return null; } } else { //否则对所有资源做监控 reportLeak(level); return new DefaultResourceLeak(obj); } }
其中reportLeak方法中完成对内存泄露的检测和报告,如下面代码所示.
private void reportLeak(Level level) { //...... // 报告生成了太多的活跃资源 int samplingInterval = level == Level.PARANOID? 1 : this.samplingInterval; if (active * samplingInterval > maxActive && loggedTooManyActive.compareAndSet(false, true)) { logger.error("LEAK: You are creating too many " + resourceType + " instances. " + resourceType + " is a shared resource that must be reused across the JVM," + "so that only a few instances are created."); } // 检测并报告之前发生的内存泄露 for (;;) { @SuppressWarnings("unchecked") //检查引用队列(为什么通过检查该队列,可以判断是否存在内存泄露) DefaultResourceLeak ref = (DefaultResourceLeak) refQueue.poll(); if (ref == null) {//队列为空,没有未报告的内存泄露或者从未发生内存泄露 break; } //清理引用 ref.clear(); if (!ref.close()) { continue; } //通过错误日志打印资源的方法调用记录,并将其保存在reportedLeaks中 String records = ref.toString(); if (reportedLeaks.putIfAbsent(records, Boolean.TRUE) == null) { if (records.isEmpty()) { logger.error("LEAK: {}.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 '-D{}={}' or call {}.setLevel()", resourceType, PROP_LEVEL, Level.ADVANCED.name().toLowerCase(), simpleClassName(this)); } else { logger.error( "LEAK: {}.release() was not called before it's garbage-collected.{}", resourceType, records); } } } }
DefaultResourceLeak的声明如下
private final class DefaultResourceLeak extends PhantomReference<Object> implements ResourceLeak{ //...... public DefaultResourceLeak(Object referent) { //使用一个静态的引用队列(refQueue)初始化 //refQueue是ResourceLeakDecetor的成员变量并由其初始化 super(referent, referent != null? refQueue : null); //...... } //...... }
可见DefaultResourceLeak是个”虚”引用类型,有别于常见的普通的”强”引用,虚引用完全不影响目标对象的垃圾回收,但是会在目标对象被VM垃圾回收时被加入到引用队列中.
在正常情况下ResourceLeak对象会所监控的资源的引用计数为0时被清理掉(不在被加入引用队列),所以一旦资源的引用计数失常,ResourceLeak对象会被加入到引用队列.例如没有成对调用ByteBuf的retain和relaease方法,导致ByteBuf没有被正常释放(对于DirectByteBuf没有及时释放内存,对于PooledByteBuf没有返回Pool),当引用队列中存在元素时意味着程序中有内存泄露发生.
ResourceLeakDetector通过检查引用队列来判断是否有内存泄露,并报告跟踪情况.
总结
Netty使用装饰器模式,为ByteBuf增加内存跟踪记录功能.利用虚引用跟踪资源被VM垃圾回收的情况,加上ByteBuf的引用计数特性,进而判断是否发生内存泄露.
相关推荐
此外,《Netty源码深入分析》视频教程还涉及了许多其他高级特性,如零拷贝、内存池管理、自定义协议栈等,这些都是开发者在实际工作中可能会遇到的问题。希望通过对本课程的学习,大家能够对Netty有一个全面而深刻的...
1. **ByteBuf**: 作为传统 ByteBuffer 的替代品,ByteBuf 提供了更高效且易用的内存管理机制,支持读写分离,避免了内存复制。 2. **Channel**: 表示网络连接,可以是 TCP、UDP 或其他协议。它提供读写操作,并处理...
- 掌握 ByteBuf 的内存管理策略,如何避免不必要的内存拷贝。 - 学习如何构建 ChannelPipeline,以及 ChannelHandler 的责任链设计。 - 分析 EventLoop 和 EventLoopGroup 的调度机制,理解它们如何确保高并发下的...
### Netty源码分析之Buffer #### Java Buffer 的相关基础知识 **1. Java 基本数据类型** Java 提供了八种基本数据类型:`byte`, `char`, `short`, `int`, `long`, `float`, `double`, `boolean`。 - **`byte`**:...
尽管提供的子文件名是"课时34:Netty源码分析总结下期预告.mp4",并未直接提供详细内容,但我们可以通过一般性的Netty源码分析来讨论一些关键知识点。 1. **NIO基础**:Netty基于Java NIO(非阻塞I/O)构建,这是...
6. **ByteBuf(字节缓冲区)**: ByteBuf是Netty自定义的字节数组,提供了更高效、灵活的内存管理,避免了频繁的Java原生数组操作导致的性能损失。 7. **Handler(处理器)**: Handler是Pipeline中的处理单元,负责...
深入源码分析,我们可以看到Netty如何优雅地处理了线程安全、内存池管理、心跳机制、解码编码、零拷贝等高级特性。例如,Netty通过内部的DirectBufferPool和HeapBufferPool实现了内存池,减少了内存分配和释放的开销...
接下来,我们谈谈 Netty 的源码分析。通过阅读 Netty 源码,我们可以深入了解其设计模式和优化策略: 1. **EventLoop(事件循环)**:Netty 使用单线程的 EventLoop 实现了事件的高效分发,减少了线程切换的开销。 ...
通过阅读源码,你可以学习到如何配置和使用这些组件,以及Netty如何处理网络I/O事件,如何进行数据编码解码,如何实现高效的内存管理和线程模型等。这对于提升网络编程技能,尤其是设计高并发、低延迟的网络应用具有...
通过分析和学习Netty 4.1的源码,我们可以深入理解其设计理念,如如何实现高效的并发处理,如何优雅地处理I/O事件,以及如何设计灵活的网络协议处理器。这对于开发高性能网络应用,特别是分布式系统和微服务架构,...
3. **ByteBuf与Buffer管理**:分析ByteBuf的设计与实现,如何高效地读写数据,以及内存管理策略。 4. **Pipeline与Handler**:解析Pipeline的结构和事件传递过程,以及自定义Handler的编写技巧。 5. **编码与解码*...
此外,源码分析有助于我们了解 Netty 如何处理网络事件、协议解析、线程调度等核心问题,对于提高开发水平和解决实际问题大有裨益。 书中可能涵盖了以下主题: - Netty 的设计理念和架构 - 异步事件驱动模型的实现...
总的来说,Netty 源码分析涉及了网络编程、并发处理、事件驱动、协议编解码等多个领域,对理解 Java 高性能网络应用开发有着重要的指导意义。通过阅读源码,我们可以更深入地了解 Netty 如何实现高效的网络通信,并...
例如,通过阅读ByteBuf的源码,我们可以学习到Netty是如何实现高效内存管理的;通过分析ChannelPipeline,我们可以理解事件在Channel之间的传递机制。 依赖包的源码同样重要,因为Netty依赖于一些第三方库来完成...
4. **强大的缓冲区管理**:ByteBuf 是 Netty 自定义的缓冲区,它提供了更高效的内存管理,支持读写分离,以及方便的缓冲区操作方法。 5. **用户友好的API**:Netty 的 API 设计简洁明了,易于理解和使用,使得...
在“Netty权威指南源码-maven版”中,你可以看到 Maven 项目的组织结构,包括 `pom.xml` 文件,这是 Maven 项目配置文件,用于管理项目依赖、构建过程等。源代码通常分布在 `src/main/java` 目录下,按照包结构组织...
#### 五、Netty源码分析实战案例 1. **ChannelHandlerContext与ChannelHandlerAdaptor详解**: - 分析`ChannelHandlerContext`的生命周期及其与`ChannelHandler`之间的交互方式。 - 深入理解`...
第四部分可能是关于 Netty 的优化与源码分析。Netty 的源码清晰且高度优化,深入研究可以帮助你更好地理解和定制框架。这部分可能涉及内存池的使用,这是 Netty 提升性能的关键因素之一,还有如何利用零拷贝技术减少...
4. **ByteBuf**:Netty 自定义的字节缓冲区,提供了比 Java NIO ByteBuffer 更高效的操作方式,支持读写分离,便于管理内存。 5. **ChannelPipeline**:一个处理链,由多个 ChannelHandler 组成,负责处理进来的...
源码分析对于深入理解Netty的工作原理至关重要。 Netty的核心概念包括以下几个方面: 1. **ByteBuf**: ByteBuf是Netty中的缓冲区类,替代了Java NIO的ByteBuffer。ByteBuf提供了更高效、更安全的字节操作,并且...