我从书中引用了一些,Netty in Action v10>添加一些上下文.
One of the tradeoffs of reference-counting is that the user have to be
carefully when consume messages. While the JVM will still be able to
GC such a message (as it is not aware of the reference-counting) this
message will not be put back in the pool from which it may be obtained
before. Thus chances are good that you will run out of resources at
one point if you do not carefully release these messages.
和一些相关的线程:
Buffer ownership in Netty 4: How is buffer life-cycle managed?
https://blog.twitter.com/2013/netty-4-at-twitter-reduced-gc-overhead
添加1
(以下是我的一些理解.)
ByteBuf可以从2个角度分类:
1. Pooled or Unpooled 2. Heap-based or Direct
所以可以有4种组合:
(a) Pooled Heap-based (b) Pooled Direct (c) Unpooled Heap-based (d) Unpooled Direct
只有(a)和(c)受到JVM GC机制的影响,因为它们是基于堆的.
在上述的< Netty in Action v10>,我认为该消息是指一个Java对象,它是(a)类别.
一个最终的规则是,如果一个Java对象是GCed,它完全消失了.以下是我认为Netty所做的:
>对于(a),Netty分配器必须欺骗JVM GC相信对象不应该被GCed.然后使用引用计数将对象移出/返回到池中.这是生命周期的另一种形式.
>对于(b),JVM GC不涉及,因为它不是基于JVM堆的.而Netty分配器需要使用引用计数将对象移出/返回到池中.
>对于(c),JVM GC全权负责控制物体的使用寿命. Netty分配器只提供用于分配对象的API.
>对于(d),不涉及JVM GC.而且不需要汇集.所以Netty分配器只需要提供分配/释放对象的API.
解决方法
当您执行I / O操作时,堆内存需要被内核处理之前复制到直接内存中.当您使用直接缓冲区时,保存该复制操作,这是使用直接缓冲区的主要优点.缺点是直接内存分配比从java堆分配更为昂贵,因此Netty引入了池化概念.
Java中的对象是一个polemic topic,但是Netty的选择似乎有所回报,而您引用的Twitter article显示了一些证据.对于分配缓冲区的具体情况,当缓冲区的大小很大时,您可以看到它在直接和堆缓冲区情况下真正带来好处.
现在要进行池化,GC在汇总时不会回收缓冲区,因为在使用缓冲区时,您的应用程序有一个或多个引用;或Netty的池有一个引用,当它刚刚被分配,还没有被给予你的应用程序,或者你的应用程序使用它,并把它还给池.
当您的应用程序在使用缓冲区并没有进一步引用它之后,会发生泄漏,不会调用release(),实际上意味着将其放回池中,如果您没有进一步的参考.在这种情况下,缓冲区最终将被垃圾回收,但Netty的池不知道.然后池会增长,相信你使用越来越多的缓冲区,这些缓冲区永远不会返回到池中.这可能会产生内存泄漏,因为即使缓冲区本身被垃圾回收,用于存储池的内部数据结构将不会.