Netty源码阅读(关于ByteBuf)

ByteBuf接口提供了一套简单易用的API,并实现了对NIO ByteBuffer的兼容,其实现有“非池化的堆内存Buffer”,“非池化的直接内存Buffer”,“池化的堆内存Buffer”,“池化的直接内存Buffer”,另外ByteBuf的实现提供了对内存泄漏不同级别的监控。

ByteBuf的内存池实现,ByteBuf对内存泄漏的监控,是我比较感兴趣的两个地方。

1.ByteBuf的内存池实现

  这里是最繁杂、最难的部分,这里后面补上。

2.ByteBuf对内存泄露的监控
Netty的内存监控是针对内存池和直接内存而言的,对于HeapByteBuf自有虚拟机完成GC。

对于内存池而言,内存泄漏产生在Buffer已经使用完,但是这块内存还没有归还给内存池,对于直接内存的内存泄漏,是Buffer使用完,直接内存没有被回收(虚拟机不会自动回收直接内存)。

ByteBuf有release和retain两个接口,由AbstractReferenceCountedByteBuf实现,分别表示对Buffer引用数目的减少和增加,当release后引用数目为0时,会对Buffer进行回收。对于内存池内的Buffer以及直接内存Buffer需要我们显式调用release进行回收。忘记调用release方法就会发生内存泄漏,特别是对于内存池而言,内存池会不断变大占用大量内存。

对于内存泄漏的监控,Netty有四个级别:Disabled,Simple,Advanced,Paranoid;分别对应不同的监控频率和性能损耗。那么Netty是如何实现内存监控的呢?

可以查看AbstractByteBufAllocator类的toLeakAwareBuffer方法,这里实际上利用了装饰器模式,以SimpleLeakAwareByteBuf为例,它持有实际起作用的ByteBuf实例,以及一个ResourceLeakTracker,ResourceLeakTracker的实现类DefaultResourceLeak继承了PhantomReference,ResourceLeakDetector将一个ReferenceQueue传入ResourceLeakTracker,这样在SimpleLeakAwareByteBuf引用被回收后,ReferenceQueue就被插入对应的refernce,我们就可以知道某个ByteBuf不再被使用了。

但是我们如何得知这个ByteBuf是否被释放了呢?

这是由一个ConcurrentHashMap来决定的,它存储的key就是DefaultResourceLeak(value应该是内存泄露的相关信息),当调用release并确定可以回收时,会把map中对应的key清除掉,这样ResourceLeakDetector会定期检查ReferenceQueue,当其中存在reference时,会查看map中是否也存在,如果也存在,那么就可以确认发生内存泄露了。
(ReferenceQueue和ConcurrentHashMap都在ResourceLeakDetector中)

简而言之,这种ByteBuf持有了一个继承PhantomReference的ResourceLeakTracker,当ByteBuf被回收时,会向ReferenceQueue传入一个对象ResourceLeakTracker;而这种ByteBuf创建(retain)时,会向一个ConcurrentHashMap存入,key为ResourceLeakTracker,value为其他信息;当ByteBuf回收(release)时,会将map中对应的记录删除。这样当ReferenceQueue中存在,而map中也存在时,就发生了内存泄漏。

关于内存泄露监控机制的不同级别,以Simple为例,我们查看AbstractByteBufAllocator的toLeakAwareBuffer方法,一路查到ResourceLeakDetector的track0方法,该方法内可以看到实际上是使用线程安全的随机数对设定的参数(默认应该是100)取余为0时,返回DefaultResourceLeak,并检查是否发生内存泄漏,否则返回null,当级别为Disable时,直接返回null,并不做检查,这样就没有性能损耗了。

你可能感兴趣的:(Netty源码阅读(关于ByteBuf))