Netty 4中的缓冲区所有权: 缓冲区的生命周期是如何管理的?

10

我一直在尝试编写一个HTTP客户端以同时获取多个(高达1k)源,这也是为了学习Netty 4而进行的练习。

我的问题是,是否有一个好的解释说明新的ByteBuf基础架构是如何工作的?谁“拥有”它们,它们是如何共享的(如果有的话)?每个ChannelPipeline中的ChannelHandler是否都有自己的ByteBuf?

这里是一个让我感到困惑的示例:

我向HTTP客户端管道添加了以下类的实例:

public class MyFilter extends MessageToMessageDecoder<HttpObject> {

    @Override
    protected Object decode(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        // do some work, but leave msg unchanged
        BufUtil.retain(msg); // Why do I need to call BufUtil.retain(msg) ???
        return msg;
}

如果我没有在msg上调用BufUtil.retain方法,它似乎会被垃圾回收(GC)并且我会遇到各种错误。

1个回答

9

HttpContent继承ReferenceCounted以跟踪其所持有的缓冲区的生命周期。当一个ReferenceCounted被实例化时,它的生命周期从1refCnt开始。如果您在其上调用retain(),则refCnt会增加。在release()上减少refCnt,并且底层资源(在这种情况下为ByteBuf)一旦refCnt变为0就会被销毁。

通常,处理程序不需要保留对其处理完成的消息的引用,因为一旦处理完成,该消息通常会被丢弃或转换为其他内容。因此,在处理程序完成后必须调用消息的release()方法。这经常容易出错,并且很容易导致资源泄漏。

为了避免难以追踪的泄漏,请扩展SimpleChannelInboundHandler,它会自动在处理完消息后调用release()

有关Netty中引用计数的更多信息,请阅读此Wiki页面。它还提供了有关如何利用Netty的缓冲区泄漏检测机制来进行故障排除的详细信息。


3
@trustin - 我也对所有这些引用计数泄漏到用户API中感到困惑和惊讶。API设计应该“做最不令人意外的事情”并且“尽可能简单”。也许你可以使用一个熟悉的Java构造:资源,就像try-with-resources一样。或者只需使消息对象默认表现得像用户期望的那样,并通过单独的接口获得高性能优化但更复杂的版本;性能并不像快速构建一个不会泄漏内存的应用程序那么重要。 - Ryan
1
@Ryan,如果在Java中有一种明确指定对象生命周期的方式,例如C++中的析构函数,我们就不需要为大多数情况引入引用计数了。然而,Java没有这样的东西,因此引用计数是管理缓冲区最有效的方法。这不太好看,但不幸的是,这是处理直接缓冲区的唯一安全方式。顺便说一下,在Netty中发现缓冲区泄漏相对容易:http://netty.io/wiki/reference-counted-objects.html#wiki-h2-10 - trustin
@smwikipedia 更新了维基页面:https://github.com/netty/netty/wiki/Reference-counted-objects (需要一些时间才能与netty.io页面同步)。如果您认为可以改进,请告诉我。 - trustin
1
@smwikipedia,“or” 应该改为“因此”,确实如此。感谢您的建议。 - trustin
1
@smwikipedia 对于你提到的帖子,我想已经有了一个很好的答案。 - trustin
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接