如果JVM GC仍然存在,为什么我们需要手动处理Netty ByteBuf的引用计数?

时间:2015-02-21 14:44:45

标签: netty

根据书籍Netty in Action v10reference 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

ADD 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 。然后使用ref count将对象移出/移回池中。 这是生命周期的另一种形式

  • 对于(b),不涉及JVM GC,因为它不是基于JVM堆的。并且Netty分配器需要使用ref计数来将对象移出/返回池中。

  • 对于(c),JVM GC全权负责控制对象的生命。 Netty allocator只提供用于分配对象的API。

  • 对于(d),不涉及JVM GC。并且不需要汇集。因此,Netty allocator只需要提供用于分配/释放对象的API。

2 个答案:

答案 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将不知道何时释放该项目)。 最好的问候