SimpleChannelInboundHandler为什么不用手动释放
他的关键是给你封装了,而且转换成你要的对象,最后会判断如果传给你了,使用后就帮你释放,否则传给下一个,不释放。
那我们以后就都用这个好啦,不用自己释放。好是好,但是他的类型匹配器创建还是比较耗性能的,会用反射来检查你的泛型,然后去线程本地变量里获取。具体的可以参考我以前的文章。
我们处理器拿到的缓冲区从哪里来
我们还是来分析下,我们拿到的缓冲区从哪里来的,当然是读出来的,对了,那我们来看看怎么读呢,首先是有读事件,然后把数据读出来:
NioByteUnsafe的read
可以看到这里会分配缓冲区,记住他的ID
是1918
:
等读完数据后开始往后传递:
最后到我们的处理器,看到了吧,是1918
,没变:
如果我们没什么操作的话,这个缓冲区就扔这里了,也没有任何释放的方法。
传到tail处理器来释放
当然我们有个办法,就是不处理但是要继续往下传,一直到tail
处理器,也就是TailContext
:
他最后会释放。
我们写出去的谁释放呢
比如我们这么写:
产生缓冲区对象2834
debug
一下,记下这个对象编号2834
:
HeadContext的write
一直传递到了HeadContext
:
NioByteUnsafe的filterOutboundMessage
到了NioByteUnsafe
的filterOutboundMessage
不是直接缓冲区的也会被封装成直接缓冲区:
NioByteUnsafe的newDirectBuffer
里面构建了新的直接缓冲区对象编码2833
,但是会释放一直传递下来的缓冲区对象:
释放2834
UnpooledHeapByteBuf的freeArray
因为是堆内的缓冲区,所以最后没做什么,等GC
来回收:
产生了新的直接缓冲区对象2833
所以现在我们写的缓冲区对象已经释放了。但是产生了新的直接缓冲区对象编号2833
,我们继续观察他有没被回收:
ChannelOutboundBuffer的addMessage
封装到了Entry
里:
这次写操作完成了,但是新创建的缓冲区没有释放,只是放进了出站缓冲区里。
AbstractChannelHandlerContext的invokeFlush0
接下来就是冲刷方法了,看看里面会不会释放刚才的2833
对象。
AbstractUnsafe的flush0
调用doWrite
,我们的2833
还在里面呢:
NioSocketChannel的doWrite
发送完了会调用这个方法:
ChannelOutboundBuffer的removeBytes
里面获取的即是2833
:
主要是里面的remove
:
释放2833
这里又把2833
给释放了:
至此,我们发送的缓冲区自动给释放了,总结下就是我们自己创建的缓冲区如果是堆内的话会把数据复制到堆外,把堆内释放了,然后把堆外的放入出站缓冲区,等冲刷的时候把堆外的数据刷出去,然后把堆外释放了,也就没有内存的泄漏了。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。