- 浏览: 978831 次
文章分类
- 全部博客 (428)
- Hadoop (2)
- HBase (1)
- ELK (1)
- ActiveMQ (13)
- Kafka (5)
- Redis (14)
- Dubbo (1)
- Memcached (5)
- Netty (56)
- Mina (34)
- NIO (51)
- JUC (53)
- Spring (13)
- Mybatis (17)
- MySQL (21)
- JDBC (12)
- C3P0 (5)
- Tomcat (13)
- SLF4J-log4j (9)
- P6Spy (4)
- Quartz (12)
- Zabbix (7)
- JAVA (9)
- Linux (15)
- HTML (9)
- Lucene (0)
- JS (2)
- WebService (1)
- Maven (4)
- Oracle&MSSQL (14)
- iText (11)
- Development Tools (8)
- UTILS (4)
- LIFE (8)
最新评论
-
Donald_Draper:
Donald_Draper 写道刘落落cici 写道能给我发一 ...
DatagramChannelImpl 解析三(多播) -
Donald_Draper:
刘落落cici 写道能给我发一份这个类的源码吗Datagram ...
DatagramChannelImpl 解析三(多播) -
lyfyouyun:
请问楼主,执行消息发送的时候,报错:Transport sch ...
ActiveMQ连接工厂、连接详解 -
ezlhq:
关于 PollArrayWrapper 状态含义猜测:参考 S ...
WindowsSelectorImpl解析一(FdMap,PollArrayWrapper) -
flyfeifei66:
打算使用xmemcache作为memcache的客户端,由于x ...
Memcached分布式客户端(Xmemcached)
netty 通道接口定义:http://donald-draper.iteye.com/blog/2392740
netty 抽象通道初始化:http://donald-draper.iteye.com/blog/2392801
netty 抽象Unsafe定义:http://donald-draper.iteye.com/blog/2393053
netty 通道Outbound缓冲区:http://donald-draper.iteye.com/blog/2393098
netty 抽象通道后续:http://donald-draper.iteye.com/blog/2393166
netty 抽象nio通道:http://donald-draper.iteye.com/blog/2393269
netty 抽象nio字节通道:http://donald-draper.iteye.com/blog/2393323
netty 抽象nio消息通道:http://donald-draper.iteye.com/blog/2393364
netty NioServerSocketChannel解析:http://donald-draper.iteye.com/blog/2393443
netty 通道配置接口定义:http://donald-draper.iteye.com/blog/2393484
netty 默认通道配置初始化:http://donald-draper.iteye.com/blog/2393504
netty 默认通道配置后续:http://donald-draper.iteye.com/blog/2393510
netty 字节buf定义:http://donald-draper.iteye.com/blog/2393813
引言
上一篇文章我们看了字节buf接口的定义,先来回顾一下:
对象引用计数器ReferenceCounted,主要记录对象的引用数量,当引用数量为0时,表示可以回收对象,在调试模式下,如果发现对象出现内存泄漏,可以用touch方法记录操作的相关信息,通过ResourceLeakDetector获取操作的相关信息,以便分析内存泄漏的原因。
字节缓存ByteBuf继承了对象引用计数器ReferenceCounted,拥有一个最大容量限制,如果用户尝试用 #capacity(int)和 #ensureWritable(int)方法,增加buf容量超过最大容量,将会抛出非法参数异常;字节buf有两个索引,一个为读索引readerIndex,一个为写索引writerIndex,读索引不能大于写索引,写索引不能小于读索引,buf可读字节数为writerIndex - readerIndex,buf可写字节数为capacity - writerIndex,buf可写的最大字节数为maxCapacity - writerIndex;
可以使用markReader/WriterIndex标记当前buf读写索引位置,resetReader/WriterIndex方法可以重回先前标记的索引位置;
当内存空间负载过度时,我们可以使用discardReadBytes丢弃一些数据,以节省空间;
我们可以使用ensureWritable检测当buf是否有足够的空间写数据;
提供了getBytes方法,可以将buf中的数据转移到目的ByteBuf,Byte数组,Nio字节buf ByteBuffer,OutputStream,聚集字节通道
GatheringByteChannel和文件通道FileChannel中,这些方法不会修改当前buf读写索引,具体是否修改目的对象索引或位置,见java doc 描述。
提供了setBytes方法,可以将源ByteBuf,Byte数组,Nio字节buf ByteBuffer,InputputStream,分散字节通道ScatteringByteChannel和文件通道FileChannel中的数据转移到当前buf中,这些方法不会修改当前buf的读写索引,至于源对象索引或位置,见java doc 描述。
提供了readBytes方法,可以将buf中的数据转移到目的ByteBuf,Byte数组,Nio字节buf ByteBuffer,OutputStream,聚集字节通道GatheringByteChannel和文件通道FileChannel中,这些方法具体会会修改当前buf读索引,至于会不会修改源对象索引或位置,见java doc 描述。
提供了writeBytes方法,可以将源ByteBuf,Byte数组,Nio字节buf ByteBuffer,
InputputStream,分散字节通道ScatteringByteChannel和文件通道FileChannel中的数据写到当前buf中,这些方法会修改当前buf的写索引,至于会不会修改源对象索引或位置,见java
doc 描述。
set*原始类型方法不会修改读写索引;
get*原始类型方法不会修改读写索引;
write*原始类型方法会修改写索引;
read*原始类型方法,会修改读索引;
字节buf中的set/get*方法不会修改当前buf的读写索引,而write*修改写索引,read*会修改读索引;
提供了copy,slice和retainSlice,duplicate和retainedDuplicate方法,用于拷贝,切割,复制当前buf数据,retained*方法会增加buf的
引用计数器;
提供nioBuffer和nioBuffers方法,用于包装当前buf可读数据为java nio ByteBuffer和ByteBuffer数组。
今天我们来看一下抽象字节buf的定义
从上面来看,字节buf内部有两个索引,一个读索引,一个写索引,两个索引标记,即读写索引对应的标记,buf的最大容量为maxCapacity;buf的构造,主要是初始化最大容量。
在抽象字节buf的内部变量声明中有一个资源泄漏探测器,定义如下:
来看内存探测器工厂
//ResourceLeakDetectorFactory
从上面可以看出,默认的资源泄漏探测器工厂创建的资源泄漏探测器类型ResourceLeakDetector。
从上面可以看出,资源泄漏探测器,探测等级有四种,关闭DISABLED,SIMPLE简单,高级ADVANCED,终极PARANOID,SIMPLE开启简单的采样资源泄漏探测,仅仅已少量的负载为代价报告是否为泄漏对象;ADVANCED开启高级采样泄漏探测,将会以高负载为代价,报告最近泄漏对象访问的地方;PARANOID开启终极采样泄漏探测,将会以高级负载为代价,报告最近泄漏对象访问的地方(仅仅测试);开启资源探测的情况下,默认等级为SIMPLE。资源探测器内部有一个探测等级和采样间隔,资源类型,泄漏报告Map( ConcurrentMap<String, Boolean>),
激活资源集合ConcurrentMap<DefaultResourceLeak, LeakEntry>,
再来看构造
从上面来看资源泄漏探测器构造,主要初始化资源类型名及探测间隔。
再来看开启资源泄漏探测功能和设置探测等级
我们来看一下枚举的ordinal方法
//Enum
回到资源泄漏探测器
再来看其他方法:
为了便于理解我们先看ResourceLeak,ResourceLeakTracker,DefaultResourceLeak的定义
//ResourceLeak
//ResourceLeakTracker
从上面可以看出,资源泄漏追踪ResourceLeakTracker,主要定义了记录当前调用者栈追踪和额外的主观信息方法,以便资源泄漏探测器可以告诉,泄漏资源最近访问的地方;关闭泄漏资源方法,以便资源泄漏探测器不在警告泄漏资源。
//DefaultResourceLeak,为资源泄漏探测器ResourceLeakDetector的内部类
我来看这一句:
//ResourceLeakHint
//StringUtil
//泄漏资源LeakEntryEntry
从上面可以看出,默认资源泄漏DefaultResourceLeak,构造过程为,首先将资源添加到引用队列,如果探测等级大于ADVANCED,则创建记录,然后添加资源泄漏对象到资源泄漏探测器的激活对象Map中。
再来看DefaultResourceLeak的记录和关闭资源操作:
从上面可以看出,记录资源泄漏,首先根据资源泄漏线索,创建泄漏线索信息,
如果泄漏线索信息集为空,或者与上次记录不同,则添加泄漏信息到泄漏记录集,
如果记录超过最大记录数,则从对头移除记录信息。
再来看关闭资源
从上面来看,关闭资源探测对象,就是从资源泄漏探测器移除资源泄漏对象。
//资源泄漏对象信息
从上面来看,报告资源泄漏信息,主要是报告由于泄漏记录数限制,丢弃的记录数,记录的资源泄漏信息,
资源泄漏创建信息。
回到资源泄漏探测器,根据资源,创建资源泄漏追踪器:
/
来看根据探测等级,报告探测结果
从上面可以看出,资源泄漏探测器,追踪资源泄漏探测信息,在采样点到达时,首先从资源泄漏引用队列,获取资源泄漏对象对象,关闭资源泄漏对象,并将探测结果添加到探测器的资源泄漏信息Map中,然后报告资源泄漏探测信息,最后创建资源探测对象。
来看需要关注的几点:
1.
2.
3.
总结:
默认的资源泄漏探测器工厂创建的资源泄漏探测器为ResourceLeakDetector。
资源泄漏探测器,探测等级有四种,关闭DISABLED,SIMPLE简单,高级ADVANCED,终极PARANOID,SIMPLE开启简单的采样资源泄漏探测,仅仅已少量的负载为代价报告是否为泄漏对象;ADVANCED开启高级采样泄漏探测,将会以高负载为代价,报告最近泄漏对象访问的地方;
PARANOID开启终极采样泄漏探测,将会以高级负载为代价,报告最近泄漏对象访问的地方(仅仅测试);开启资源探测的情况下,默认等级为SIMPLE。资源探测器内部有一个探测等级和采样间隔,资源类型,泄漏报告Map( ConcurrentMap<String, Boolean>),激活资源集合ConcurrentMap<DefaultResourceLeak, LeakEntry>。
资源泄漏探测器构造,主要初始化资源类型名及探测间隔。
资源泄漏追踪ResourceLeakTracker,主要定义了记录当前调用者栈追踪和额外的主观信息方法,
以便资源泄漏探测器可以告诉,泄漏资源最近访问的地方;关闭泄漏资源方法,以便资源泄漏探测器不在警告泄漏资源。
默认资源泄漏DefaultResourceLeak,构造过程为,首先将资源添加到引用队列,如果探测等级大于ADVANCED,则创建记录,然后添加资源泄漏对象到资源泄漏探测器的激活对象Map中。
记录资源泄漏,首先根据资源泄漏线索,创建泄漏线索信息,如果泄漏线索信息集为空,或者与上次记录不同,则添加泄漏信息到泄漏记录集,如果记录超过最大记录数,则从对头移除记录信息。
关闭资源探测对象,就是从资源泄漏探测器移除资源泄漏对象。
报告资源泄漏信息,主要是报告由于泄漏记录数限制,丢弃的记录数,记录的资源泄漏信息,
资源泄漏创建信息。
资源泄漏探测器,追踪资源泄漏探测信息,在采样点到达时,首先从资源泄漏引用队列,获取资源泄漏对象对象,关闭资源泄漏对象,并将探测结果添加到探测器的资源泄漏信息Map中,然后报告资源泄漏探测信息,最后创建资源探测对象。
附:
//PhantomReference
//Reference
netty 抽象通道初始化:http://donald-draper.iteye.com/blog/2392801
netty 抽象Unsafe定义:http://donald-draper.iteye.com/blog/2393053
netty 通道Outbound缓冲区:http://donald-draper.iteye.com/blog/2393098
netty 抽象通道后续:http://donald-draper.iteye.com/blog/2393166
netty 抽象nio通道:http://donald-draper.iteye.com/blog/2393269
netty 抽象nio字节通道:http://donald-draper.iteye.com/blog/2393323
netty 抽象nio消息通道:http://donald-draper.iteye.com/blog/2393364
netty NioServerSocketChannel解析:http://donald-draper.iteye.com/blog/2393443
netty 通道配置接口定义:http://donald-draper.iteye.com/blog/2393484
netty 默认通道配置初始化:http://donald-draper.iteye.com/blog/2393504
netty 默认通道配置后续:http://donald-draper.iteye.com/blog/2393510
netty 字节buf定义:http://donald-draper.iteye.com/blog/2393813
引言
上一篇文章我们看了字节buf接口的定义,先来回顾一下:
对象引用计数器ReferenceCounted,主要记录对象的引用数量,当引用数量为0时,表示可以回收对象,在调试模式下,如果发现对象出现内存泄漏,可以用touch方法记录操作的相关信息,通过ResourceLeakDetector获取操作的相关信息,以便分析内存泄漏的原因。
字节缓存ByteBuf继承了对象引用计数器ReferenceCounted,拥有一个最大容量限制,如果用户尝试用 #capacity(int)和 #ensureWritable(int)方法,增加buf容量超过最大容量,将会抛出非法参数异常;字节buf有两个索引,一个为读索引readerIndex,一个为写索引writerIndex,读索引不能大于写索引,写索引不能小于读索引,buf可读字节数为writerIndex - readerIndex,buf可写字节数为capacity - writerIndex,buf可写的最大字节数为maxCapacity - writerIndex;
可以使用markReader/WriterIndex标记当前buf读写索引位置,resetReader/WriterIndex方法可以重回先前标记的索引位置;
当内存空间负载过度时,我们可以使用discardReadBytes丢弃一些数据,以节省空间;
我们可以使用ensureWritable检测当buf是否有足够的空间写数据;
提供了getBytes方法,可以将buf中的数据转移到目的ByteBuf,Byte数组,Nio字节buf ByteBuffer,OutputStream,聚集字节通道
GatheringByteChannel和文件通道FileChannel中,这些方法不会修改当前buf读写索引,具体是否修改目的对象索引或位置,见java doc 描述。
提供了setBytes方法,可以将源ByteBuf,Byte数组,Nio字节buf ByteBuffer,InputputStream,分散字节通道ScatteringByteChannel和文件通道FileChannel中的数据转移到当前buf中,这些方法不会修改当前buf的读写索引,至于源对象索引或位置,见java doc 描述。
提供了readBytes方法,可以将buf中的数据转移到目的ByteBuf,Byte数组,Nio字节buf ByteBuffer,OutputStream,聚集字节通道GatheringByteChannel和文件通道FileChannel中,这些方法具体会会修改当前buf读索引,至于会不会修改源对象索引或位置,见java doc 描述。
提供了writeBytes方法,可以将源ByteBuf,Byte数组,Nio字节buf ByteBuffer,
InputputStream,分散字节通道ScatteringByteChannel和文件通道FileChannel中的数据写到当前buf中,这些方法会修改当前buf的写索引,至于会不会修改源对象索引或位置,见java
doc 描述。
set*原始类型方法不会修改读写索引;
get*原始类型方法不会修改读写索引;
write*原始类型方法会修改写索引;
read*原始类型方法,会修改读索引;
字节buf中的set/get*方法不会修改当前buf的读写索引,而write*修改写索引,read*会修改读索引;
提供了copy,slice和retainSlice,duplicate和retainedDuplicate方法,用于拷贝,切割,复制当前buf数据,retained*方法会增加buf的
引用计数器;
提供nioBuffer和nioBuffers方法,用于包装当前buf可读数据为java nio ByteBuffer和ByteBuffer数组。
今天我们来看一下抽象字节buf的定义
/** * A skeletal implementation of a buffer. */ public abstract class AbstractByteBuf extends ByteBuf { private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractByteBuf.class); private static final String PROP_MODE = "io.netty.buffer.bytebuf.checkAccessible"; private static final boolean checkAccessible; static { checkAccessible = SystemPropertyUtil.getBoolean(PROP_MODE, true); if (logger.isDebugEnabled()) { logger.debug("-D{}: {}", PROP_MODE, checkAccessible); } } //内存泄漏探测器 static final ResourceLeakDetector<ByteBuf> leakDetector = ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ByteBuf.class); int readerIndex;//读索引 int writerIndex;//写索引 private int markedReaderIndex;//读索引标记 private int markedWriterIndex;//写索引标记 private int maxCapacity;//最大容量 protected AbstractByteBuf(int maxCapacity) { if (maxCapacity < 0) { throw new IllegalArgumentException("maxCapacity: " + maxCapacity + " (expected: >= 0)"); } this.maxCapacity = maxCapacity; }: }
从上面来看,字节buf内部有两个索引,一个读索引,一个写索引,两个索引标记,即读写索引对应的标记,buf的最大容量为maxCapacity;buf的构造,主要是初始化最大容量。
在抽象字节buf的内部变量声明中有一个资源泄漏探测器,定义如下:
public abstract class AbstractByteBuf extends ByteBuf { //内存泄漏探测器 static final ResourceLeakDetector<ByteBuf> leakDetector = ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ByteBuf.class); ... }
来看内存探测器工厂
//ResourceLeakDetectorFactory
/** * This static factory should be used to load {@link ResourceLeakDetector}s as needed */ public abstract class ResourceLeakDetectorFactory { private static final InternalLogger logger = InternalLoggerFactory.getInstance(ResourceLeakDetectorFactory.class); //默认资源泄漏探测器工厂 private static volatile ResourceLeakDetectorFactory factoryInstance = new DefaultResourceLeakDetectorFactory(); /** * Get the singleton instance of this factory class. *获取资源泄漏探测器工厂 * @return the current {@link ResourceLeakDetectorFactory} */ public static ResourceLeakDetectorFactory instance() { return factoryInstance; } /** * Set the factory's singleton instance. This has to be called before the static initializer of the * {@link ResourceLeakDetector} is called by all the callers of this factory. That is, before initializing a * Netty Bootstrap. *设置资源泄漏探测器工厂单例。此方法必须在素有的工作调用者,初始化ResourceLeakDetector前调用, 即在netty的引导启动配置前初始化 * @param factory the instance that will become the current {@link ResourceLeakDetectorFactory}'s singleton */ public static void setResourceLeakDetectorFactory(ResourceLeakDetectorFactory factory) { factoryInstance = ObjectUtil.checkNotNull(factory, "factory"); } /** * Returns a new instance of a {@link ResourceLeakDetector} with the given resource class. *使用给定的资源类,创建要给资源泄漏探测器 * @param resource the resource class used to initialize the {@link ResourceLeakDetector} * @param <T> the type of the resource class * @return a new instance of {@link ResourceLeakDetector} */ public final <T> ResourceLeakDetector<T> newResourceLeakDetector(Class<T> resource) { return newResourceLeakDetector(resource, ResourceLeakDetector.DEFAULT_SAMPLING_INTERVAL); } /** * Returns a new instance of a {@link ResourceLeakDetector} with the given resource class. *使用给定的资源类和采样间隔,创建要给资源泄漏探测器 * @param resource the resource class used to initialize the {@link ResourceLeakDetector} * @param samplingInterval the interval on which sampling takes place * @param <T> the type of the resource class * @return a new instance of {@link ResourceLeakDetector} */ @SuppressWarnings("deprecation") public <T> ResourceLeakDetector<T> newResourceLeakDetector(Class<T> resource, int samplingInterval) { return newResourceLeakDetector(resource, ResourceLeakDetector.DEFAULT_SAMPLING_INTERVAL, Long.MAX_VALUE); } /** * @deprecated Use {@link #newResourceLeakDetector(Class, int)} instead. * <p> * Returns a new instance of a {@link ResourceLeakDetector} with the given resource class. *待子类扩展 * @param resource the resource class used to initialize the {@link ResourceLeakDetector} * @param samplingInterval the interval on which sampling takes place,采样间隔 * @param maxActive This is deprecated and will be ignored. 此参数已经丢弃,将会被忽略 * @param <T> the type of the resource class 资源类,类型 * @return a new instance of {@link ResourceLeakDetector} */ @Deprecated public abstract <T> ResourceLeakDetector<T> newResourceLeakDetector( Class<T> resource, int samplingInterval, long maxActive); /** * Default implementation that loads custom leak detector via system property 默认资源泄漏探测器工厂 */ private static final class DefaultResourceLeakDetectorFactory extends ResourceLeakDetectorFactory { private final Constructor<?> obsoleteCustomClassConstructor; private final Constructor<?> customClassConstructor; DefaultResourceLeakDetectorFactory() { String customLeakDetector; try { //在当前线程相同访问控制权限下,获取系统io.netty.customResourceLeakDetector属性配置 customLeakDetector = AccessController.doPrivileged(new PrivilegedAction<String>() { @Override public String run() { return SystemPropertyUtil.get("io.netty.customResourceLeakDetector"); } }); } catch (Throwable cause) { logger.error("Could not access System property: io.netty.customResourceLeakDetector", cause); customLeakDetector = null; } if (customLeakDetector == null) { //如果系统泄漏测器配置为空,则obsoleteCustom和custom构造为空 obsoleteCustomClassConstructor = customClassConstructor = null; } else { //否则初始泄漏探测器构造 obsoleteCustomClassConstructor = obsoleteCustomClassConstructor(customLeakDetector); customClassConstructor = customClassConstructor(customLeakDetector); } } private static Constructor<?> obsoleteCustomClassConstructor(String customLeakDetector) { try { //加载泄漏探测器类 final Class<?> detectorClass = Class.forName(customLeakDetector, true, PlatformDependent.getSystemClassLoader()); if (ResourceLeakDetector.class.isAssignableFrom(detectorClass)) { //构造资源泄漏探测器 return detectorClass.getConstructor(Class.class, int.class, long.class); } else { logger.error("Class {} does not inherit from ResourceLeakDetector.", customLeakDetector); } } catch (Throwable t) { logger.error("Could not load custom resource leak detector class provided: {}", customLeakDetector, t); } return null; } private static Constructor<?> customClassConstructor(String customLeakDetector) { try { //加载泄漏探测器类 final Class<?> detectorClass = Class.forName(customLeakDetector, true, PlatformDependent.getSystemClassLoader()); //构造资源泄漏探测器 if (ResourceLeakDetector.class.isAssignableFrom(detectorClass)) { return detectorClass.getConstructor(Class.class, int.class); } else { logger.error("Class {} does not inherit from ResourceLeakDetector.", customLeakDetector); } } catch (Throwable t) { logger.error("Could not load custom resource leak detector class provided: {}", customLeakDetector, t); } return null; } //根据资源类和采样间隔,创建资源泄漏探测器 @Override public <T> ResourceLeakDetector<T> newResourceLeakDetector(Class<T> resource, int samplingInterval) { if (customClassConstructor != null) { try { @SuppressWarnings("unchecked") //使用customClassConstructor创建资源泄漏探测器 ResourceLeakDetector<T> leakDetector = (ResourceLeakDetector<T>) customClassConstructor.newInstance(resource, samplingInterval); logger.debug("Loaded custom ResourceLeakDetector: {}", customClassConstructor.getDeclaringClass().getName()); return leakDetector; } catch (Throwable t) { logger.error( "Could not load custom resource leak detector provided: {} with the given resource: {}", customClassConstructor.getDeclaringClass().getName(), resource, t); } } //直接创建资源泄漏探测器ResourceLeakDetector ResourceLeakDetector<T> resourceLeakDetector = new ResourceLeakDetector<T>(resource, samplingInterval); logger.debug("Loaded default ResourceLeakDetector: {}", resourceLeakDetector); return resourceLeakDetector; } @SuppressWarnings("deprecation") @Override public <T> ResourceLeakDetector<T> newResourceLeakDetector(Class<T> resource, int samplingInterval, long maxActive) { if (obsoleteCustomClassConstructor != null) { try { //使用obsoleteCustomClassConstructor创建资源泄漏探测器 @SuppressWarnings("unchecked") ResourceLeakDetector<T> leakDetector = (ResourceLeakDetector<T>) obsoleteCustomClassConstructor.newInstance( resource, samplingInterval, maxActive); logger.debug("Loaded custom ResourceLeakDetector: {}", obsoleteCustomClassConstructor.getDeclaringClass().getName()); return leakDetector; } catch (Throwable t) { logger.error( "Could not load custom resource leak detector provided: {} with the given resource: {}", obsoleteCustomClassConstructor.getDeclaringClass().getName(), resource, t); } } //直接创建资源泄漏探测器ResourceLeakDetector ResourceLeakDetector<T> resourceLeakDetector = new ResourceLeakDetector<T>(resource, samplingInterval, logger.debug("Loaded default ResourceLeakDetector: {}", resourceLeakDetector); return resourceLeakDetector; } } }
从上面可以看出,默认的资源泄漏探测器工厂创建的资源泄漏探测器类型ResourceLeakDetector。
public class ResourceLeakDetector<T> { private static final String PROP_LEVEL_OLD = "io.netty.leakDetectionLevel"; private static final String PROP_LEVEL = "io.netty.leakDetection.level"; private static final Level DEFAULT_LEVEL = Level.SIMPLE;//默认探测登记 private static final String PROP_MAX_RECORDS = "io.netty.leakDetection.maxRecords"; private static final int DEFAULT_MAX_RECORDS = 4;//默认最大的资源泄漏记录线索数 private static final int MAX_RECORDS;//最大的资源泄漏记录线索数 /** * Represents the level of resource leak detection. 资源泄漏等级 */ public enum Level { /** * Disables resource leak detection. 关闭资源泄漏探测 */ DISABLED, /** * Enables simplistic sampling resource leak detection which reports there is a leak or not, * at the cost of small overhead (default). 开启简单的采样资源泄漏探测,仅仅已少量的负载为代价报告是否为泄漏对象 */ SIMPLE, /** * Enables advanced sampling resource leak detection which reports where the leaked object was accessed * recently at the cost of high overhead. 开启高级采样泄漏探测,将会以高负载为代价,报告最近泄漏对象访问的地方 */ ADVANCED, /** * Enables paranoid resource leak detection which reports where the leaked object was accessed recently, * at the cost of the highest possible overhead (for testing purposes only). 开启终极采样泄漏探测,将会以高级负载为代价,报告最近泄漏对象访问的地方 */ PARANOID; /** * Returns level based on string value. Accepts also string that represents ordinal number of enum. *返回泄漏探测等级字符串。同时接受表示enum原始序号的字符串。 即根据等级字符串,返回对应的泄漏探测等级 * @param levelStr - level string : DISABLED, SIMPLE, ADVANCED, PARANOID. Ignores case. * @return corresponding level or SIMPLE level in case of no match. */ static Level parseLevel(String levelStr) { String trimmedLevelStr = levelStr.trim(); for (Level l : values()) { if (trimmedLevelStr.equalsIgnoreCase(l.name()) || trimmedLevelStr.equals(String.valueOf(l.ordinal()))) { return l; } } return DEFAULT_LEVEL; } } private static Level level; //当前static资源泄漏等级 private static final InternalLogger logger = InternalLoggerFactory.getInstance(ResourceLeakDetector.class); //初始化资源泄漏探测状态及探测等级 static { final boolean disabled; //获取系统资源泄漏探测配置,判断是否开启资源探测 if (SystemPropertyUtil.get("io.netty.noResourceLeakDetection") != null) { disabled = SystemPropertyUtil.getBoolean("io.netty.noResourceLeakDetection", false); logger.debug("-Dio.netty.noResourceLeakDetection: {}", disabled); logger.warn( "-Dio.netty.noResourceLeakDetection is deprecated. Use '-D{}={}' instead.", PROP_LEVEL, DEFAULT_LEVEL.name().toLowerCase()); } else { disabled = false; } //关闭,则等级为DISABLED,否则为默认等级 Level defaultLevel = disabled? Level.DISABLED : DEFAULT_LEVEL; // First read old property name String levelStr = SystemPropertyUtil.get(PROP_LEVEL_OLD, defaultLevel.name()); // If new property name is present, use it levelStr = SystemPropertyUtil.get(PROP_LEVEL, levelStr); Level level = Level.parseLevel(levelStr); MAX_RECORDS = SystemPropertyUtil.getInt(PROP_MAX_RECORDS, DEFAULT_MAX_RECORDS); ResourceLeakDetector.level = level; if (logger.isDebugEnabled()) { logger.debug("-D{}: {}", PROP_LEVEL, level.name().toLowerCase()); logger.debug("-D{}: {}", PROP_MAX_RECORDS, MAX_RECORDS); } } // Should be power of two. 默认采样间隔 static final int DEFAULT_SAMPLING_INTERVAL = 128; /** the collection of active resources 激活资源集合*/ private final ConcurrentMap<DefaultResourceLeak, LeakEntry> allLeaks = PlatformDependent.newConcurrentHashMap(); //资源写对象引用队列 private final ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>(); //资源泄漏信息报告 private final ConcurrentMap<String, Boolean> reportedLeaks = PlatformDependent.newConcurrentHashMap(); private final String resourceType;//资源类型 private final int samplingInterval;//采样间隔 }
从上面可以看出,资源泄漏探测器,探测等级有四种,关闭DISABLED,SIMPLE简单,高级ADVANCED,终极PARANOID,SIMPLE开启简单的采样资源泄漏探测,仅仅已少量的负载为代价报告是否为泄漏对象;ADVANCED开启高级采样泄漏探测,将会以高负载为代价,报告最近泄漏对象访问的地方;PARANOID开启终极采样泄漏探测,将会以高级负载为代价,报告最近泄漏对象访问的地方(仅仅测试);开启资源探测的情况下,默认等级为SIMPLE。资源探测器内部有一个探测等级和采样间隔,资源类型,泄漏报告Map( ConcurrentMap<String, Boolean>),
激活资源集合ConcurrentMap<DefaultResourceLeak, LeakEntry>,
再来看构造
/** * @deprecated use {@link ResourceLeakDetectorFactory#newResourceLeakDetector(Class, int, long)}. 根据资源类型创建资源泄漏探测器 */ @Deprecated public ResourceLeakDetector(Class<?> resourceType) { this(simpleClassName(resourceType)); } /** * @deprecated use {@link ResourceLeakDetectorFactory#newResourceLeakDetector(Class, int, long)}. 根据资源类型名创建资源泄漏探测器 */ @Deprecated public ResourceLeakDetector(String resourceType) { this(resourceType, DEFAULT_SAMPLING_INTERVAL, Long.MAX_VALUE); } /** * @deprecated Use {@link ResourceLeakDetector#ResourceLeakDetector(Class, int)}. 此方法已丢弃,请使用#ResourceLeakDetector(Class, int)方法 * <p> * This should not be used directly by users of {@link ResourceLeakDetector}. * Please use {@link ResourceLeakDetectorFactory#newResourceLeakDetector(Class)} * or {@link ResourceLeakDetectorFactory#newResourceLeakDetector(Class, int, long)} *用于不可直接调用此方法,请使用ResourceLeakDetectorFactory#newResourceLeakDetector(Class) 或ResourceLeakDetectorFactory#newResourceLeakDetector(Class, int, long)方法 * @param maxActive This is deprecated and will be ignored. */ @Deprecated public ResourceLeakDetector(Class<?> resourceType, int samplingInterval, long maxActive) { this(resourceType, samplingInterval); } /** * This should not be used directly by users of {@link ResourceLeakDetector}. * Please use {@link ResourceLeakDetectorFactory#newResourceLeakDetector(Class)} * or {@link ResourceLeakDetectorFactory#newResourceLeakDetector(Class, int, long)} */ @SuppressWarnings("deprecation") public ResourceLeakDetector(Class<?> resourceType, int samplingInterval) { this(simpleClassName(resourceType), samplingInterval, Long.MAX_VALUE); } /** * @deprecated use {@link ResourceLeakDetectorFactory#newResourceLeakDetector(Class, int, long)}. * <p> * @param maxActive This is deprecated and will be ignored. */ @Deprecated public ResourceLeakDetector(String resourceType, int samplingInterval, long maxActive) { if (resourceType == null) { throw new NullPointerException("resourceType"); } this.resourceType = resourceType; this.samplingInterval = samplingInterval; }
从上面来看资源泄漏探测器构造,主要初始化资源类型名及探测间隔。
再来看开启资源泄漏探测功能和设置探测等级
/** * @deprecated Use {@link #setLevel(Level)} instead. 开启资源泄漏探测 */ @Deprecated public static void setEnabled(boolean enabled) { setLevel(enabled? Level.SIMPLE : Level.DISABLED); }
/** * Returns {@code true} if resource leak detection is enabled. 判断是否开启资源泄漏探测 */ public static boolean isEnabled() { //如果探测等级大于DISABLED,则开启 return getLevel().ordinal() > Level.DISABLED.ordinal(); }
我们来看一下枚举的ordinal方法
//Enum
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { /** * The name of this enum constant, as declared in the enum declaration. * Most programmers should use the {@link #toString} method rather than * accessing this field. 枚举名,一般使用#toString方法获取,而不是直接方法此field */ private final String name; /** * The ordinal of this enumeration constant (its position * in the enum declaration, where the initial constant is assigned * an ordinal of zero). *枚举常量在定义中的位置 * Most programmers will have no use for this field. It is designed * for use by sophisticated enum-based data structures, such as * {@link java.util.EnumSet} and {@link java.util.EnumMap}. */ private final int ordinal; /** * Returns the name of this enum constant, exactly as declared in its * enum declaration. * * <b>Most programmers should use the {@link #toString} method in * preference to this one, as the toString method may return * a more user-friendly name.</b> This method is designed primarily for * use in specialized situations where correctness depends on getting the * exact name, which will not vary from release to release. * * @return the name of this enum constant */ public final String name() { return name; } /** * Returns the ordinal of this enumeration constant (its position * in its enum declaration, where the initial constant is assigned * an ordinal of zero). *获取枚举常量在定义中的位置 * Most programmers will have no use for this method. It is * designed for use by sophisticated enum-based data structures, such * as {@link java.util.EnumSet} and {@link java.util.EnumMap}. * * @return the ordinal of this enumeration constant */ public final int ordinal() { return ordinal; } ... }
回到资源泄漏探测器
/** * Sets the resource leak detection level. 设置资源泄漏探测等级 */ public static void setLevel(Level level) { if (level == null) { throw new NullPointerException("level"); } ResourceLeakDetector.level = level; } /** * Returns the current resource leak detection level. 获取资源泄漏探测等级 */ public static Level getLevel() { return level; }
再来看其他方法:
/** * Creates a new {@link ResourceLeak} which is expected to be closed via {@link ResourceLeak#close()} when the * related resource is deallocated. *创建一个资源泄漏ResourceLeak,当相关资源释放时,希望通过ResourceLeak#close关闭,已丢弃 * @return the {@link ResourceLeak} or {@code null} * @deprecated use {@link #track(Object)} */ @Deprecated public final ResourceLeak open(T obj) { return track0(obj); } /** * Creates a new {@link ResourceLeakTracker} which is expected to be closed via * {@link ResourceLeakTracker#close(Object)} when the related resource is deallocated. *创建一个资源泄漏ResourceLeakTracker,当相关资源释放时,希望通过ResourceLeakTracker#close(Object)关闭对象 * @return the {@link ResourceLeakTracker} or {@code null} */ public final ResourceLeakTracker<T> track(T obj) { return track0(obj); } private DefaultResourceLeak track0(T obj) { ... }
为了便于理解我们先看ResourceLeak,ResourceLeakTracker,DefaultResourceLeak的定义
//ResourceLeak
package io.netty.util; /** * @deprecated please use {@link ResourceLeakTracker} as it may lead to false-positives. 已丢弃,请使用ResourceLeakTracker */ @Deprecated public interface ResourceLeak { /** * Records the caller's current stack trace so that the {@link ResourceLeakDetector} can tell where the leaked * resource was accessed lastly. This method is a shortcut to {@link #record(Object) record(null)}. 记录当前调用者栈追踪,以便资源泄漏探测器可以告诉,泄漏资源最近访问的地方。此方法为 #record(Object)的快捷方式record(null) */ void record(); /** * Records the caller's current stack trace and the specified additional arbitrary information * so that the {@link ResourceLeakDetector} can tell where the leaked resource was accessed lastly. 记录当前调用者栈追踪和额外的主观信息,以便资源泄漏探测器可以告诉,泄漏资源最近访问的地方。 */ void record(Object hint); /** * Close the leak so that {@link ResourceLeakDetector} does not warn about leaked resources. *关闭泄漏资源,以便资源泄漏探测器不在警告泄漏资源。 * @return {@code true} if called first time, {@code false} if called already 第一次调用返回true,否则为false */ boolean close(); }
//ResourceLeakTracker
package io.netty.util; public interface ResourceLeakTracker<T> { /** * Records the caller's current stack trace so that the {@link ResourceLeakDetector} can tell where the leaked * resource was accessed lastly. This method is a shortcut to {@link #record(Object) record(null)}. 记录当前调用者栈追踪,以便资源泄漏探测器可以告诉,泄漏资源最近访问的地方。此方法为 #record(Object)的快捷方式record(null) */ void record(); /** * Records the caller's current stack trace and the specified additional arbitrary information * so that the {@link ResourceLeakDetector} can tell where the leaked resource was accessed lastly. 记录当前调用者栈追踪和额外的主观信息,以便资源泄漏探测器可以告诉,泄漏资源最近访问的地方。 */ void record(Object hint); /** * Close the leak so that {@link ResourceLeakTracker} does not warn about leaked resources. * After this method is called a leak associated with this ResourceLeakTracker should not be reported. *关闭泄漏资源,以便资源泄漏探测器不在警告泄漏资源。在方法调用后,泄漏资源关联的ResourceLeakTracker不应该 上报。 * @return {@code true} if called first time, {@code false} if called already 第一次调用返回true,否则为false */ boolean close(T trackedObject); }
从上面可以看出,资源泄漏追踪ResourceLeakTracker,主要定义了记录当前调用者栈追踪和额外的主观信息方法,以便资源泄漏探测器可以告诉,泄漏资源最近访问的地方;关闭泄漏资源方法,以便资源泄漏探测器不在警告泄漏资源。
//DefaultResourceLeak,为资源泄漏探测器ResourceLeakDetector的内部类
@SuppressWarnings("deprecation") private final class DefaultResourceLeak extends PhantomReference<Object> implements ResourceLeakTracker<T>, ResourceLeak { private final String creationRecord;//创建记录 private final Deque<String> lastRecords = new ArrayDeque<String>();//记录队列 private final int trackedHash; private int removedRecords;移除记录计数器 DefaultResourceLeak(Object referent) { super(referent, refQueue); assert referent != null; // Store the hash of the tracked object to later assert it in the close(...) method. // It's important that we not store a reference to the referent as this would disallow it from // be collected via the PhantomReference. //获取对象引用hash值 trackedHash = System.identityHashCode(referent); Level level = getLevel(); if (level.ordinal() >= Level.ADVANCED.ordinal()) { //如果探测等级大于ADVANCED,则创建记录 creationRecord = newRecord(null, 3); } else { creationRecord = null; } //将当前资源泄漏对象方法激活对象Map中 allLeaks.put(this, LeakEntry.INSTANCE); } }
我来看这一句:
//如果探测登记大于ADVANCED,则创建记录 creationRecord = newRecord(null, 3);
static String newRecord(Object hint, int recordsToSkip) { StringBuilder buf = new StringBuilder(4096); // Append the hint first if available. if (hint != null) { buf.append("\tHint: "); // Prefer a hint string to a simple string form. //如果对象时资源追踪线索,添加线索信息 if (hint instanceof ResourceLeakHint) { buf.append(((ResourceLeakHint) hint).toHintString()); } else { buf.append(hint); } buf.append(NEWLINE); } // Append the stack trace. StackTraceElement[] array = new Throwable().getStackTrace(); for (StackTraceElement e: array) { //跳过前3个栈追踪 if (recordsToSkip > 0) { recordsToSkip --; } else { String estr = e.toString(); // Strip the noisy stack trace elements. boolean excluded = false; //剔除栈追踪噪音 for (String exclusion: STACK_TRACE_ELEMENT_EXCLUSIONS) { if (estr.startsWith(exclusion)) { excluded = true; break; } } if (!excluded) { buf.append('\t'); buf.append(estr); buf.append(NEWLINE); } } } return buf.toString(); }
//ResourceLeakHint
package io.netty.util; /** * A hint object that provides human-readable message for easier resource leak tracking. 资源泄漏线索,提供可读的资源泄漏追踪信息 */ public interface ResourceLeakHint { /** * Returns a human-readable message that potentially enables easier resource leak tracking. */ String toHintString(); }
private static final String[] STACK_TRACE_ELEMENT_EXCLUSIONS = { "io.netty.util.ReferenceCountUtil.touch(", "io.netty.buffer.AdvancedLeakAwareByteBuf.touch(", "io.netty.buffer.AbstractByteBufAllocator.toLeakAwareBuffer(", "io.netty.buffer.AdvancedLeakAwareByteBuf.recordLeakNonRefCountingOperation(" };
//StringUtil
package io.netty.util.internal; /** * String utility class. */ public final class StringUtil { public static final String EMPTY_STRING = ""; public static final String NEWLINE = SystemPropertyUtil.get("line.separator", "\n"); public static final char DOUBLE_QUOTE = '\"'; public static final char COMMA = ','; public static final char LINE_FEED = '\n'; public static final char CARRIAGE_RETURN = '\r'; public static final char TAB = '\t'; ... }
//泄漏资源LeakEntryEntry
private static final class LeakEntry { static final LeakEntry INSTANCE = new LeakEntry();//内部实例 private static final int HASH = System.identityHashCode(INSTANCE);//实例hash值 private LeakEntry() { } @Override public int hashCode() { return HASH; } @Override public boolean equals(Object obj) { return obj == this; } }
从上面可以看出,默认资源泄漏DefaultResourceLeak,构造过程为,首先将资源添加到引用队列,如果探测等级大于ADVANCED,则创建记录,然后添加资源泄漏对象到资源泄漏探测器的激活对象Map中。
再来看DefaultResourceLeak的记录和关闭资源操作:
@Override public void record() { record0(null, 3); } @Override public void record(Object hint) { record0(hint, 3); } private void record0(Object hint, int recordsToSkip) { if (creationRecord != null) { //根据资源泄漏线索,创建泄漏线索信息 String value = newRecord(hint, recordsToSkip); synchronized (lastRecords) { int size = lastRecords.size(); if (size == 0 || !lastRecords.getLast().equals(value)) { //如果泄漏线索信息集为空,或者与上次记录不同,则添加泄漏信息到泄漏记录集 lastRecords.add(value); } if (size > MAX_RECORDS) { //如果记录超过最大记录数,则从对头移除记录信息 lastRecords.removeFirst(); ++removedRecords;//更新移除记录计数器 } } } }
从上面可以看出,记录资源泄漏,首先根据资源泄漏线索,创建泄漏线索信息,
如果泄漏线索信息集为空,或者与上次记录不同,则添加泄漏信息到泄漏记录集,
如果记录超过最大记录数,则从对头移除记录信息。
再来看关闭资源
@Override public boolean close() { // Use the ConcurrentMap remove method, which avoids allocating an iterator. //从资源泄漏探测器移除资源泄漏对象 return allLeaks.remove(this, LeakEntry.INSTANCE); } @Override public boolean close(T trackedObject) { // Ensure that the object that was tracked is the same as the one that was passed to close(...). assert trackedHash == System.identityHashCode(trackedObject); // We need to actually do the null check of the trackedObject after we close the leak because otherwise // we may get false-positives reported by the ResourceLeakDetector. This can happen as the JIT / GC may // be able to figure out that we do not need the trackedObject anymore and so already enqueue it for // collection before we actually get a chance to close the enclosing ResourceLeak. return close() && trackedObject != null; }
从上面来看,关闭资源探测对象,就是从资源泄漏探测器移除资源泄漏对象。
//资源泄漏对象信息
@Override public String toString() { if (creationRecord == null) { return EMPTY_STRING; } final Object[] array; final int removedRecords; synchronized (lastRecords) { array = lastRecords.toArray(); removedRecords = this.removedRecords; } StringBuilder buf = new StringBuilder(16384).append(NEWLINE); if (removedRecords > 0) { //报告,由于泄漏记录数限制,丢弃的记录数 buf.append("WARNING: ") .append(removedRecords) .append(" leak records were discarded because the leak record count is limited to ") .append(MAX_RECORDS) .append(". Use system property ") .append(PROP_MAX_RECORDS) .append(" to increase the limit.") .append(NEWLINE); } buf.append("Recent access records: ") .append(array.length) .append(NEWLINE); //报告,记录的资源泄漏信息 if (array.length > 0) { for (int i = array.length - 1; i >= 0; i --) { buf.append('#') .append(i + 1) .append(':') .append(NEWLINE) .append(array[i]); } } //报告资源泄漏创建信息 buf.append("Created at:") .append(NEWLINE) .append(creationRecord); buf.setLength(buf.length() - NEWLINE.length()); return buf.toString(); }
从上面来看,报告资源泄漏信息,主要是报告由于泄漏记录数限制,丢弃的记录数,记录的资源泄漏信息,
资源泄漏创建信息。
回到资源泄漏探测器,根据资源,创建资源泄漏追踪器:
/
** * Creates a new {@link ResourceLeakTracker} which is expected to be closed via * {@link ResourceLeakTracker#close(Object)} when the related resource is deallocated. *创建一个资源泄漏ResourceLeakTracker,当相关资源释放时,希望通过ResourceLeakTracker#close(Object)关闭对象 * @return the {@link ResourceLeakTracker} or {@code null} */ public final ResourceLeakTracker<T> track(T obj) { return track0(obj); } //追踪资源泄漏对象线索 private DefaultResourceLeak track0(T obj) { Level level = ResourceLeakDetector.level; if (level == Level.DISABLED) { return null; } if (level.ordinal() < Level.PARANOID.ordinal()) { if ((PlatformDependent.threadLocalRandom().nextInt(samplingInterval)) == 0) { //根据探测等级,报告探测结果 reportLeak(level); //创建资源泄漏对象 return new DefaultResourceLeak(obj); } else { return null; } } else { reportLeak(level); return new DefaultResourceLeak(obj); } }
来看根据探测等级,报告探测结果
//根据探测等级,报告探测结果 reportLeak(level);
private void reportLeak(Level level) { if (!logger.isErrorEnabled()) { //如果日志Error级别没有开启,则将资源探测对象的引用队列中,poll探测对象,并关闭 for (;;) { @SuppressWarnings("unchecked") DefaultResourceLeak ref = (DefaultResourceLeak) refQueue.poll(); if (ref == null) { break; } ref.close(); } return; } // Detect and report previous leaks. 报告内存泄漏请求 for (;;) { @SuppressWarnings("unchecked") // DefaultResourceLeak ref = (DefaultResourceLeak) refQueue.poll(); if (ref == null) { break; } //清除引用 ref.clear(); if (!ref.close()) { //如果资源泄漏对象没关闭,则跳出当前循环 continue; } //获取资源泄漏探测信息 String records = ref.toString(); //记录资源泄漏信息 if (reportedLeaks.putIfAbsent(records, Boolean.TRUE) == null) { if (records.isEmpty()) { //资源泄漏记录为空,则报告不可追踪 reportUntracedLeak(resourceType); } else { //报告资源泄漏信息 reportTracedLeak(resourceType, records); } } } }
从上面可以看出,资源泄漏探测器,追踪资源泄漏探测信息,在采样点到达时,首先从资源泄漏引用队列,获取资源泄漏对象对象,关闭资源泄漏对象,并将探测结果添加到探测器的资源泄漏信息Map中,然后报告资源泄漏探测信息,最后创建资源探测对象。
来看需要关注的几点:
1.
//清除引用 //Reference public abstract class Reference<T> { private T referent; /* Treated specially by GC */ ReferenceQueue<? super T> queue; Reference next; transient private Reference<T> discovered; /* used by VM */ /* Object used to synchronize with the garbage collector. The collector * must acquire this lock at the beginning of each collection cycle. It is * therefore critical that any code holding this lock complete as quickly * as possible, allocate no new objects, and avoid calling user code. */ static private class Lock { }; private static Lock lock = new Lock(); /* List of References waiting to be enqueued. The collector adds * References to this list, while the Reference-handler thread removes * them. This list is protected by the above lock object. */ private static Reference pending = null; /** * Clears this reference object. Invoking this method will not cause this * object to be enqueued. * 清除对象引用 * <p> This method is invoked only by Java code; when the garbage collector * clears references it does so directly, without invoking this method. */ public void clear() { this.referent = null; } ... }
2.
/** * This method is called when a traced leak is detected. It can be overridden for tracking how many times leaks * have been detected. 报告资源泄漏信息 */ protected void reportTracedLeak(String resourceType, String records) { logger.error( "LEAK: {}.release() was not called before it's garbage-collected. " + "See http://netty.io/wiki/reference-counted-objects.html for more information.{}", resourceType, records); }
3.
/** * This method is called when an untraced leak is detected. It can be overridden for tracking how many times leaks * have been detected. 报告不可追踪 */ protected void reportUntracedLeak(String resourceType) { 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() " + "See http://netty.io/wiki/reference-counted-objects.html for more information.", resourceType, PROP_LEVEL, Level.ADVANCED.name().toLowerCase(), simpleClassName(this)); }
总结:
默认的资源泄漏探测器工厂创建的资源泄漏探测器为ResourceLeakDetector。
资源泄漏探测器,探测等级有四种,关闭DISABLED,SIMPLE简单,高级ADVANCED,终极PARANOID,SIMPLE开启简单的采样资源泄漏探测,仅仅已少量的负载为代价报告是否为泄漏对象;ADVANCED开启高级采样泄漏探测,将会以高负载为代价,报告最近泄漏对象访问的地方;
PARANOID开启终极采样泄漏探测,将会以高级负载为代价,报告最近泄漏对象访问的地方(仅仅测试);开启资源探测的情况下,默认等级为SIMPLE。资源探测器内部有一个探测等级和采样间隔,资源类型,泄漏报告Map( ConcurrentMap<String, Boolean>),激活资源集合ConcurrentMap<DefaultResourceLeak, LeakEntry>。
资源泄漏探测器构造,主要初始化资源类型名及探测间隔。
资源泄漏追踪ResourceLeakTracker,主要定义了记录当前调用者栈追踪和额外的主观信息方法,
以便资源泄漏探测器可以告诉,泄漏资源最近访问的地方;关闭泄漏资源方法,以便资源泄漏探测器不在警告泄漏资源。
默认资源泄漏DefaultResourceLeak,构造过程为,首先将资源添加到引用队列,如果探测等级大于ADVANCED,则创建记录,然后添加资源泄漏对象到资源泄漏探测器的激活对象Map中。
记录资源泄漏,首先根据资源泄漏线索,创建泄漏线索信息,如果泄漏线索信息集为空,或者与上次记录不同,则添加泄漏信息到泄漏记录集,如果记录超过最大记录数,则从对头移除记录信息。
关闭资源探测对象,就是从资源泄漏探测器移除资源泄漏对象。
报告资源泄漏信息,主要是报告由于泄漏记录数限制,丢弃的记录数,记录的资源泄漏信息,
资源泄漏创建信息。
资源泄漏探测器,追踪资源泄漏探测信息,在采样点到达时,首先从资源泄漏引用队列,获取资源泄漏对象对象,关闭资源泄漏对象,并将探测结果添加到探测器的资源泄漏信息Map中,然后报告资源泄漏探测信息,最后创建资源探测对象。
附:
//PhantomReference
package java.lang.ref; /** * Phantom reference objects, which are enqueued after the collector * determines that their referents may otherwise be reclaimed. Phantom * references are most often used for scheduling pre-mortem cleanup actions in * a more flexible way than is possible with the Java finalization mechanism. * * <p> If the garbage collector determines at a certain point in time that the * referent of a phantom reference is <a * href="package-summary.html#reachability">phantom reachable</a>, then at that * time or at some later time it will enqueue the reference. * * <p> In order to ensure that a reclaimable object remains so, the referent of * a phantom reference may not be retrieved: The <code>get</code> method of a * phantom reference always returns <code>null</code>. * * <p> Unlike soft and weak references, phantom references are not * automatically cleared by the garbage collector as they are enqueued. An * object that is reachable via phantom references will remain so until all * such references are cleared or themselves become unreachable. * * @author Mark Reinhold * @since 1.2 */ public class PhantomReference<T> extends Reference<T> { /** * Returns this reference object's referent. Because the referent of a * phantom reference is always inaccessible, this method always returns * <code>null</code>. * * @return <code>null</code> */ public T get() { return null; } /** * Creates a new phantom reference that refers to the given object and * is registered with the given queue. * * <p> It is possible to create a phantom reference with a <tt>null</tt> * queue, but such a reference is completely useless: Its <tt>get</tt> * method will always return null and, since it does not have a queue, it * will never be enqueued. * * @param referent the object the new phantom reference will refer to * @param q the queue with which the reference is to be registered, * or <tt>null</tt> if registration is not required */ public PhantomReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); } }
//Reference
/** * Abstract base class for reference objects. This class defines the * operations common to all reference objects. Because reference objects are * implemented in close cooperation with the garbage collector, this class may * not be subclassed directly. * * @author Mark Reinhold * @since 1.2 */ public abstract class Reference<T> { /* A Reference instance is in one of four possible internal states: * * Active: Subject to special treatment by the garbage collector. Some * time after the collector detects that the reachability of the * referent has changed to the appropriate state, it changes the * instance's state to either Pending or Inactive, depending upon * whether or not the instance was registered with a queue when it was * created. In the former case it also adds the instance to the * pending-Reference list. Newly-created instances are Active. * * Pending: An element of the pending-Reference list, waiting to be * enqueued by the Reference-handler thread. Unregistered instances * are never in this state. * * Enqueued: An element of the queue with which the instance was * registered when it was created. When an instance is removed from * its ReferenceQueue, it is made Inactive. Unregistered instances are * never in this state. * * Inactive: Nothing more to do. Once an instance becomes Inactive its * state will never change again. * * The state is encoded in the queue and next fields as follows: * * Active: queue = ReferenceQueue with which instance is registered, or * ReferenceQueue.NULL if it was not registered with a queue; next = * null. * * Pending: queue = ReferenceQueue with which instance is registered; * next = Following instance in queue, or this if at end of list. * * Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance * in queue, or this if at end of list. * * Inactive: queue = ReferenceQueue.NULL; next = this. * * With this scheme the collector need only examine the next field in order * to determine whether a Reference instance requires special treatment: If * the next field is null then the instance is active; if it is non-null, * then the collector should treat the instance normally. * * To ensure that concurrent collector can discover active Reference * objects without interfering with application threads that may apply * the enqueue() method to those objects, collectors should link * discovered objects through the discovered field. */ private T referent; /* Treated specially by GC */ ReferenceQueue<? super T> queue; Reference next; transient private Reference<T> discovered; /* used by VM */ /* Object used to synchronize with the garbage collector. The collector * must acquire this lock at the beginning of each collection cycle. It is * therefore critical that any code holding this lock complete as quickly * as possible, allocate no new objects, and avoid calling user code. */ static private class Lock { }; private static Lock lock = new Lock(); /* List of References waiting to be enqueued. The collector adds * References to this list, while the Reference-handler thread removes * them. This list is protected by the above lock object. */ private static Reference pending = null; /* High-priority thread to enqueue pending References */ private static class ReferenceHandler extends Thread { ReferenceHandler(ThreadGroup g, String name) { super(g, name); } public void run() { for (;;) { Reference r; synchronized (lock) { if (pending != null) { r = pending; Reference rn = r.next; pending = (rn == r) ? null : rn; r.next = r; } else { try { lock.wait(); } catch (InterruptedException x) { } continue; } } // Fast path for cleaners if (r instanceof Cleaner) { ((Cleaner)r).clean(); continue; } ReferenceQueue q = r.queue; if (q != ReferenceQueue.NULL) q.enqueue(r); } } } static { ThreadGroup tg = Thread.currentThread().getThreadGroup(); for (ThreadGroup tgn = tg; tgn != null; tg = tgn, tgn = tg.getParent()); Thread handler = new ReferenceHandler(tg, "Reference Handler"); /* If there were a special system-only priority greater than * MAX_PRIORITY, it would be used here */ handler.setPriority(Thread.MAX_PRIORITY); handler.setDaemon(true); handler.start(); } /* -- Referent accessor and setters -- */ /** * Returns this reference object's referent. If this reference object has * been cleared, either by the program or by the garbage collector, then * this method returns <code>null</code>. * * @return The object to which this reference refers, or * <code>null</code> if this reference object has been cleared */ public T get() { return this.referent; } /** * Clears this reference object. Invoking this method will not cause this * object to be enqueued. * * <p> This method is invoked only by Java code; when the garbage collector * clears references it does so directly, without invoking this method. */ public void clear() { this.referent = null; } /* -- Queue operations -- */ /** * Tells whether or not this reference object has been enqueued, either by * the program or by the garbage collector. If this reference object was * not registered with a queue when it was created, then this method will * always return <code>false</code>. * * @return <code>true</code> if and only if this reference object has * been enqueued */ public boolean isEnqueued() { /* In terms of the internal states, this predicate actually tests whether the instance is either Pending or Enqueued */ synchronized (this) { return (this.queue != ReferenceQueue.NULL) && (this.next != null); } } /** * Adds this reference object to the queue with which it is registered, * if any. * * <p> This method is invoked only by Java code; when the garbage collector * enqueues references it does so directly, without invoking this method. * * @return <code>true</code> if this reference object was successfully * enqueued; <code>false</code> if it was already enqueued or if * it was not registered with a queue when it was created */ public boolean enqueue() { return this.queue.enqueue(this); } /* -- Constructors -- */ Reference(T referent) { this(referent, null); } Reference(T referent, ReferenceQueue<? super T> queue) { this.referent = referent; this.queue = (queue == null) ? ReferenceQueue.NULL : queue; } }
发表评论
-
netty NioSocketChannel解析
2017-09-29 12:50 1312netty 抽象BootStrap定义:http://dona ... -
netty Pooled字节buf分配器
2017-09-28 13:00 2050netty 字节buf定义:http://donald-dra ... -
netty Unpooled字节buf分配器
2017-09-26 22:01 2436netty 字节buf定义:http://donald-dra ... -
netty 抽象字节buf分配器
2017-09-26 08:43 1310netty 字节buf定义:http:// ... -
netty 复合buf概念
2017-09-25 22:31 1304netty 字节buf定义:http://donald-dra ... -
netty 抽象字节buf引用计数器
2017-09-22 12:48 1591netty 字节buf定义:http://donald-dra ... -
netty 抽象字节buf解析
2017-09-22 09:00 1841netty 通道接口定义:http://donald-drap ... -
netty 字节buf定义
2017-09-20 08:31 2823netty 通道接口定义:http://donald-drap ... -
netty 默认通道配置后续
2017-09-18 08:36 2174netty 通道接口定义:http://donald-drap ... -
netty 默认通道配置初始化
2017-09-17 22:51 2032netty 通道接口定义:http://donald-drap ... -
netty 通道配置接口定义
2017-09-17 14:51 1076netty 通道接口定义:http://donald-drap ... -
netty NioServerSocketChannel解析
2017-09-16 13:01 1875netty ServerBootStrap解析:http:// ... -
netty 抽象nio消息通道
2017-09-15 15:30 1215netty 通道接口定义:http:/ ... -
netty 抽象nio字节通道
2017-09-14 22:39 1200netty 通道接口定义:http:/ ... -
netty 抽象nio通道解析
2017-09-14 17:23 953netty 通道接口定义:http://donald-drap ... -
netty 抽象通道后续
2017-09-13 22:40 1305netty Inboudn/Outbound通道Inv ... -
netty 通道Outbound缓冲区
2017-09-13 14:31 2187netty 通道接口定义:http:/ ... -
netty 抽象Unsafe定义
2017-09-12 21:24 1070netty 通道接口定义:http:/ ... -
netty 抽象通道初始化
2017-09-11 12:56 1850netty 管道线定义-ChannelPipeline:htt ... -
netty 通道接口定义
2017-09-10 15:36 1870netty Inboudn/Outbound通道Invoker ...
相关推荐
这个“netty资源包.zip”文件包含了开发人员在使用 Netty 实现通信时所需的各种 jar 包。这些库提供了丰富的功能,使得构建网络应用变得更加简单。 Netty 的核心组件包括以下几个部分: 1. **ByteBuf**: Netty ...
这个“Netty网络框架学习资源(珍藏版)”压缩包包含了一份详细的Netty教学电子书,它涵盖了Netty的基础概念、通信协议、API使用以及加解密方法等多个方面,对于想要深入理解和掌握Netty的开发者来说,是一份非常...
这个“Netty开发资源.rar”压缩包包含了Netty的不同版本及其相关的开发资源和文档,对于学习和使用Netty进行网络编程非常有帮助。 首先,我们来看Netty-4.0和Netty-4.1这两个开发资源包。Netty的每个主要版本都会...
本资源包包含 Netty 的全方位学习材料,包括源码、笔记和学习文档,旨在帮助开发者深入理解和掌握 Netty。 一、Netty 源码解析 Netty 的源码是理解其工作原理的关键。通过阅读源码,我们可以了解到 Netty 如何实现...
添加io.netty的maven库, 在com.zhao的包下,可以自行修改 有较多的注释,可以学习使用
在高级特性部分,书籍会涉及Netty的编解码器,如LineBasedFrameDecoder用于处理以换行符分隔的协议,以及Delimiters用于识别特定分隔符的协议。同时,自定义编解码器的编写也是进阶学习的重点,这使得Netty能适应...
这个视频是闪电侠出品的netty ,主要讲解netty 源码的。百度云盘资源。直接用txt打开就可以了。
2. **Netty架构**:Netty采用了反应器模式,包含Bootstrap(引导类)、ServerBootstrap(服务器引导类)、Channel(通道)、EventLoop(事件循环)、Pipeline(处理链)等组件,构建了高效的事件驱动模型。...
这个压缩包包含的是Netty 4.0.0.CR3版本的相关资源,包括源码、示例以及API文档,对于学习和理解Netty的工作原理以及如何在实际项目中应用Netty非常有帮助。 首先,让我们来详细探讨一下Netty的核心特性: 1. **...
《Netty实战》这本书是针对Java网络编程框架Netty的一本深入实践教程,旨在帮助读者掌握Netty的核心特性和实际应用。Netty是一款高性能、异步事件驱动的网络应用程序框架,广泛应用于各种分布式系统、微服务架构以及...
Netty基础,用于学习Netty,参考黑马程序员的netty教程
Netty框架中LineBasedFrameDecoder分隔符解码器解决考虑TCP的粘包与拆包问题。依次编译bytebuf中的可读字符,判断看是否有“\n”或者“\r\n”,如果有,就以此位置为结束位置,从可读索引到结束位置区间的字节就组成...
2. **高效的数据编码与解码**:Netty 提供了多种编解码器,如 LineBasedFrameDecoder、LengthFieldBasedFrameDecoder 等,简化了数据包的处理。 3. **线程模型**:Netty 的 Boss-Worker 线程模型,Boss 线程负责...
《Netty实战》是针对Java开发者的一本技术指南,它深入介绍了如何利用Netty这个高性能、异步事件驱动的网络应用程序框架来构建高效且可扩展的网络应用。Netty不仅简化了网络编程的复杂性,还提供了丰富的特性和组件...
这个“netty-netty-4.1.69.Final.tar.gz”文件是Netty的最新稳定版本,版本号为4.1.69.Final,它是一个压缩包文件,通常包含源码、编译后的类库、文档和其他相关资源。 Netty的核心特点包括: 1. **异步事件驱动**...
Netty的异步特性使得RocketMQ能处理高并发场景,而其高效的内存管理机制降低了系统的资源消耗。 在实际学习过程中,可以先从阅读Netty的官方文档开始,理解基本概念和API。然后,通过分析RocketMQ的源码,观察它是...
ChannelHandlerAdapter 4.X版本和5.X版本的差别很大。ChannelRead是属于5.X版本的4.X版本没有这个方法,所以如果要用ChannelRead。可以更换5.X版本的Netty。
Netty提供了一套丰富的编解码器(Codec),包括ByteToMessageDecoder和MessageToByteEncoder,用于在网络数据传输中进行数据编码和解码。这些编解码器支持多种协议,如HTTP、WebSocket、FTP等,极大地简化了协议处理...
集成时需注意线程模型的兼容性,确保 Netty 的工作线程和 Spring 的任务调度器协同工作,避免资源冲突。 6. **Spring MVC 与 Netty**: 如果你希望在 Netty 中使用 Spring MVC 来处理 HTTP 请求,可以创建一个 ...
《深入浅出Netty》是一本专注于讲解Netty框架的编程指南,非常适合初学者入门。Netty是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。这本书通过详实的代码案例,...