如果JVM GC仍然存在,为什么我们需要手动处理Netty ByteBuf的引用计数? [英] Why do we need to manually handle reference counting for Netty ByteBuf if JVM GC is still in place?

查看:419
本文介绍了如果JVM GC仍然存在,为什么我们需要手动处理Netty ByteBuf的引用计数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据书Netty in Action v10reference counting用于处理ByteBuf的合并.但是JVM并不了解净引用计数,因此JVM仍可以GC ByteBuf.如果是这样,为什么我们仍然需要关心引用计数并手动调用release()方法?

According to the book Netty in Action v10, reference counting is used to handle the pooling of ByteBuf. But JVM is not aware of the netty reference counting, so JVM can still GC the ByteBuf. If so, why do we still need to care about the reference counting and manually call release() method?

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

I quoted some from the book < Netty in Action v10 > to add some context.

引用计数的折衷之一是用户必须 消费消息时要小心.虽然JVM仍然能够 GC这样的消息(因为它不知道引用计数) 消息不会被放回可能从中获取消息的池中 前.因此,您很有可能会耗尽以下资源: 如果您不小心释放这些消息,则有一点.

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.

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

And some related threads: Buffer ownership in Netty 4: How is buffer life-cycle managed?

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

(下面是我的一些理解.)

(Below is some more of my understanding.)

A ByteBuf可以从两个角度进行分类:

A ByteBuf can be categorized from 2 perspectives:

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机制的影响,因为它们是基于堆的.

Only (a) and (c) are affected by JVM GC mechanism because they are heap-based.

在以上< Netty in Action v10>,我认为消息表示一个Java对象,属于(a)类别.

In the above quotation from < Netty in Action v10 >, I think the message means a Java object, which is in (a) category.

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

One ultimate rule is, if a Java object is GCed, it's totally gone. So below is what I think Netty does:

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

  • For (a), Netty allocator MUST trick JVM GC into believing the object should never be GCed. And then use ref counting to move the object out of/back into the pool. This is another form of life cycle.

对于(b),不涉及JVM GC,因为它不是基于JVM堆的. Netty分配器需要使用引用计数将对象移入/移出池.

For (b), JVM GC is not involved as it is not JVM Heap-based. And Netty allocator need to use ref counting to move the object out of/back into the pool.

对于(c),JVM GC承担控制对象寿命的全部责任. Netty分配器仅提供用于分配对象的API.

For (c), JVM GC takes full responsibility to control the life of object. Netty allocator just provide API for allocating object.

对于(d),不涉及JVM GC.并且不需要池化.因此Netty分配器只需要提供用于分配/释放对象的API.

For (d), JVM GC is not involved. And no pooling is needed. So Netty allocator only needs provide API for allocating/releasing the object.

推荐答案

直接缓冲区由垃圾收集器间接释放.我将让您通读此问题的答案,以了解其发生方式: Are Java DirectByteBuffer包装器的垃圾被收集了吗?

Direct buffers are indirectly freed by the Garbage Collector. I will let you read through the answer to this question to understand how that happens: Are Java DirectByteBuffer wrappers garbage collected?

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

Heap buffers need to be copied to the direct memory before being handled by the kernel, when you're performing I/O operations. When you use direct buffers you save that copy operation and that's the main advantage of using direct buffers. A drawback is that direct memory allocation is reasonably more expensive then allocations from the java heap, so Netty introduced the pooling concept.

在Java中池化对象是争论性主题,但是Netty选择这样做似乎已经获得回报,并且 Twitter文章您引用的内容表明了这一点.对于分配缓冲区的特殊情况,当缓冲区的大小很大时,您会发现它在直接缓冲区和堆缓冲区中确实带来了好处.

Pooling objects in Java is a polemic topic, but the Netty choice for doing so seems to have paid off and the Twitter article you cited shows some evidence of that. For the particular case of allocating buffers, when the size of the buffer is large, you can see that it really brings a benefit both in the direct and heap buffer cases.

现在可以进行池化了,GC不会在池化时回收缓冲区,因为在使用缓冲区时,您的应用程序对它有一个或多个引用.当Netty的池刚刚被分配但尚未分配给您的应用程序时,或者在您的应用程序使用它并将其返回给池之后,Netty的池就会对其进行引用.

Now for pooling, the GC doesn't reclaim the buffer when they are pooled, because either your application has one or several references to it, while you're using the buffer; or Netty's pool has a reference to it, when it has just been allocated and has not yet been given to your application or after your application used it and gave it back to the pool.

如果您的应用程序在使用了缓冲区并且未保留对其的任何进一步引用后,未调用release(),则实际上发生泄漏,这实际上意味着将其放回池中,如果您没有任何进一步的参考.在这种情况下,缓冲区最终将被垃圾回收,但是Netty的池对此一无所知.然后,该池将增长,并相信您正在使用越来越多的缓冲区,这些缓冲区永远不会返回到池中.这可能会导致内存泄漏,因为即使缓冲区本身被垃圾回收,用于存储池的内部数据结构也不会.

Leaks will happen when your application, after having used a buffer and not keeping any further reference to it, doesn't call release(), what actually means put it back into the pool, if you don't have any further reference to it. In such case, the buffer will eventually be garbage collected, but Netty's pool won't know about it. The pool will then grow believing that you're using more and more buffers that are never returned to the pool. That will probably generate a memory leak because, even if the buffers themselves are garbage collected, the internal data structures used to store the pool will not.

这篇关于如果JVM GC仍然存在,为什么我们需要手动处理Netty ByteBuf的引用计数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆