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

smw*_*dia 17 netty

根据这本书Netty in Action v10, reference counting是用来处理汇集的ByteBuf.但是JVM并不知道netty引用计数,所以JVM仍然可以GC ByteBuf.如果是这样,为什么我们仍然需要关心引用计数和手动调用release()方法?

我引用了<Netty in Action v10>中的一些来添加一些上下文.

引用计数的权衡之一是用户在使用消息时必须小心.虽然JVM仍然能够GC这样的消息(因为它不知道引用计数),但是这个消息不会被放回到之前可能获得它的池中.因此,如果您不仔细发布这些消息,那么在某一点上您将耗尽资源的可能性很大.

还有一些相关的线程: Netty 4中的缓冲区所有权:缓冲区生命周期是如何管理的?

https://blog.twitter.com/2013/netty-4-at-twitter-reduced-gc-overhead

添加1

(以下是我的理解.)

A ByteBuf可以从2个角度分类:

1. Pooled or Unpooled
2. Heap-based or Direct
Run Code Online (Sandbox Code Playgroud)

所以可以有4种组合:

(a) Pooled Heap-based
(b) Pooled Direct
(c) Unpooled Heap-based
(d) Unpooled Direct
Run Code Online (Sandbox Code Playgroud)

只有(a)和(c)受JVM GC机制的影响,因为它们是基于堆的.

在<Netty in Action v10>的上述引文中,我认为该消息意味着一个Java对象,它位于(a)类别中.

一个最终的规则是,如果一个Java对象是GCed,它就完全消失了.以下是我认为Netty的作用:

  • 对于(a),Netty分配器必须诱使JVM GC相信该对象永远不应该被GC.然后使用ref count将对象移出/移回池中.这是生命周期的另一种形式.

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

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

  • 对于(d),不涉及JVM GC.并且不需要汇集.所以Netty allocator只需要提供API来分配/释放对象.

Leo*_*mes 25

垃圾收集器间接释放直接缓冲区.我会让你仔细阅读这个问题的答案,以了解这是怎么回事:Java DirectByteBuffer包装器是垃圾收集的吗?

当您执行I/O操作时,堆缓冲区需要在内核处理之前复制到直接内存.使用直接缓冲区时,可以保存该复制操作,这是使用直接缓冲区的主要优点.缺点是直接内存分配比从Java堆分配要贵得多,因此Netty引入了池化概念.

在Java中汇集对象是一个极端主题,但Netty选择这样做似乎已经得到了回报,而您引用的Twitter文章显示了一些证据.对于分配缓冲区的特殊情况,当缓冲区的大小很大时,您可以看到它确实在直接缓冲区和堆缓冲区情况下都带来了好处.

现在对于池化,GC在池化时不会回收缓冲区,因为当你使用缓冲区时,你的应用程序有一个或几个引用它; 或者Netty的池有一个对它的引用,当它刚刚被分配并且尚未提供给您的应用程序时,或者在您的应用程序使用它并将其返回池之后.

如果您的应用程序在使用缓冲区并且未对其进行任何进一步引用之后不会调用release(),实际上意味着将其放回池中,如果您没有任何进一步的引用,则会发生泄漏.在这种情况下,缓冲区最终将被垃圾收集,但Netty的池将不会知道它.然后,池将逐渐相信您正在使用越来越多的永不返回池中的缓冲区.这可能会产生内存泄漏,因为即使缓冲区本身是垃圾回收,用于存储池的内部数据结构也不会.

  • @LeoGomes:“在这种情况下,缓冲区最终将被垃圾收集,但 Netty 的池不会知道这一点。” 当池持有对缓冲区的引用时,如何对缓冲区进行垃圾回收? (2认同)