vlambda博客
学习文章列表

Netty也是人写的,从4.0早期版本里的bug看应该如何正确清空数组


本篇文章大概800字,阅读时间大约5分钟


本篇文章是对前面Netty线程图像相关文章的一个重新总结,并修改了一些错误,还是希望用面试题的形式来全面总结Netty的设计思想和使用的细节,本文review了Netty早期的数组清空的bug,以及总结了正确的套路。

Netty也是人写的,从4.0早期版本里的bug看应该如何正确清空数组

接上一篇,Netty的NioEventLoop的事件循环机制——run方法里,默认情况下,处理I/O事件,是调用了processSelectedKeys();方法,如下:

Netty也是人写的,从4.0早期版本里的bug看应该如何正确清空数组

processSelectedKeys();方法内部会执行优化的processSelectedKeys方法

processSelectedKeysOptimized(selectedKeys.flip());


跟进这个优化的方法:

Netty也是人写的,从4.0早期版本里的bug看应该如何正确清空数组

Netty这里的处理有一个值得我们留意的地方。在<<Effective Java>>里也有对这一技巧的论述——即清空数组时,需要及时释放过期的对象引用。


上图的黄色1处,通过selectedKeys.keys[i]取出一个SelectionKey后,把该位置的对象引用及时的置为了null,该处的注释也写的很清楚——这样做的目的是让GC及时的清理,避免OOM。


假设一个场景:NioEventLoop的NIO线程,PS.这里强调很多次了,NIO线程特指的是NioEventLoop聚合的JDK的Thread,该线程对象永远不变,有时候也有人叫NioEventLoop线程,都可以,不纠结这些术语。


言归正传:如果在run里,每次检测Channel时,平均轮询出1000个I/O事件,在高峰期轮询出10000个,那么selectedKeys的数组就需要扩容,扩容机制是复制数组,大小为原来的2倍。假设每次处理key后,不置selectedKeys[i]为空,那么高峰期一过,该数组的“活动部分(active portion)”之外的任何引用都是过期的,活动部分是索引下标小于size的元素组成。即这些保存在selectedKeys数组尾部的SelectionKey将一直无法被回收,这些key都有附加的Netty封装的Channel,它们的内存占用很大,堆积很容易导致内存泄漏,这类问题的解决方法很简单——即一旦对象引用过期,显示的将它们设置为null即可,这个bug存在于Netty的早期版本,在Netty的4.0.19.Final版本中已被修复。


以上,也不用感觉不可思议,Netty怎么还犯这种垃圾错误?!这说明是人都会犯错,甚至犯一些低级错误,即使是享誉盛名的开源框架或者是社区的开发者们。


END


点亮在看,你最好看

~

阅读原文,获得更多精彩内容