根据书籍Netty in Action v10
,reference counting
用于处理ByteBuf
的汇集。但是JVM并不知道netty引用计数,因此JVM仍然可以GC ByteBuf
。如果是这样,为什么我们仍然需要关心引用计数并手动调用release()
方法?
我从书中引用了一些< Netty in Action v10>添加一些上下文。
引用计数的一个权衡是用户必须这样做 消费消息时要小心。虽然JVM仍然可以 GC这样的消息(因为它不知道引用计数)这个 消息不会被放回到可以获取它的池中 之前。因此,您将耗尽资源的可能性很大 如果您不小心发布这些消息,请注意一点。
以及一些相关主题: Buffer ownership in Netty 4: How is buffer life-cycle managed?
https://blog.twitter.com/2013/netty-4-at-twitter-reduced-gc-overhead
(以下是我的理解。)
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 。然后使用ref count将对象移出/移回池中。 这是生命周期的另一种形式。
对于(b),不涉及JVM GC,因为它不是基于JVM堆的。并且Netty分配器需要使用ref计数来将对象移出/返回池中。
对于(c),JVM GC全权负责控制对象的生命。 Netty allocator只提供用于分配对象的API。
对于(d),不涉及JVM GC。并且不需要汇集。因此,Netty allocator只需要提供用于分配/释放对象的API。
答案 0 :(得分:22)
垃圾收集器间接释放直接缓冲区。我会让你仔细阅读这个问题的答案,以了解这是怎么回事:Are Java DirectByteBuffer wrappers garbage collected?
当您执行I / O操作时,堆缓冲区需要在内核处理之前复制到直接内存。使用直接缓冲区时,可以保存该复制操作,这是使用直接缓冲区的主要优点。缺点是直接内存分配比从Java堆分配要贵得多,因此Netty引入了池化概念。
在Java中汇集对象是polemic topic,但Netty选择这样做似乎已经得到了回报,而您引用的Twitter article显示了一些证据。对于分配缓冲区的特殊情况,当缓冲区的大小很大时,您可以看到它确实在直接缓冲区和堆缓冲区情况下都带来了好处。
现在对于池化,GC在池化时不会回收缓冲区,因为你的应用程序有一个或几个引用它,而你正在使用缓冲区;或者Netty的池有一个对它的引用,当它刚刚被分配并且尚未提供给您的应用程序时,或者在您的应用程序使用它并将其返回池之后。
当您的应用程序在使用缓冲区并且未对其进行任何进一步引用后,不会调用release()
,实际上意味着将其放回池中> em>,如果你没有进一步的参考。在这种情况下,缓冲区最终将被垃圾收集,但Netty的池子不知道它。然后,池将逐渐相信您正在使用越来越多的永不返回池中的缓冲区。这可能会产生内存泄漏,因为即使缓冲区本身是垃圾回收,用于存储池的内部数据结构也不会。
答案 1 :(得分:2)
ByteBuf正在使用off堆内存,因此它对GC不可见。这就是为什么你需要更新引用计数(否则netty将不知道何时释放该项目)。 最好的问候