繁体   English   中英

Netty-通过PooledByteBufAllocator分配可重用缓冲区的列表

[英]Netty - Allocating a List of reusable buffers via PooledByteBufAllocator

描绘一个Netty应用程序,该应用程序在同一通道上从多个不同数据源中发送数据。

在发送的数据/输出字节上,每个源都将由特定的标头+来自源的数据来标识。

我打算使用一小部分可重复使用的缓冲区,其内容(字节)会随着来自给定源的每个新数据块而更新。 这种为每个数据源设置少量可重用缓冲区的方法应节省分配直接缓冲区的时间,并只需一遍又一遍地简单地将(给定源的)标头和新数据写入(每个给定源缓冲区的)同一存储区。 然后,将缓冲区的内容从两个包装的(组合的)缓冲区之一发送/写入套接字通道(取决于条件)。

我正在尝试这种方法,希望可以为每个DataSource类内的池创建5个直接缓冲区(内存块)并继续重用它。 但是,尝试在代码中任何其他地方使用缓冲区时,我遇到异常。 异常:io.netty.util.IllegalReferenceCountException:refCnt:0

public class DataSource {

    final List<ByteBuf> BUFFERS = new ArrayList<>();

    private void alocateHeaderBuffers() {

        ByteBufAllocator pbba = PooledByteBufAllocator.DEFAULT;
        BUFFERS.add(0, pbba.directBuffer(32, 32));    // buffer of fixed size  32 bytes - header 1
        BUFFERS.add(1, pbba.directBuffer(128, 128));  // buffer of fixed size 128 bytes - header 2
        BUFFERS.add(2, pbba.directBuffer(256));       // buffer of minimum size 256 (can grow) - for data of various size
        // Two combined buffers using - Unpooled.wrappedBuffer():
        // Combine 2 buffers: 0 + 2
        // and buffers:       1 + 2
        BUFFERS.add(3, Unpooled.wrappedBuffer(BUFFERS.get(0), BUFFERS.get(2)));
        BUFFERS.add(4, Unpooled.wrappedBuffer(BUFFERS.get(1), BUFFERS.get(2)));
    }
}

异常说明-如果我这样做:

    ByteBuf bb1 = BUFFERS.get(0);
    bb1.writeBytes(headerPrefix); // headerPrefix = 4 bytes array

调试器将bb1显示为“已释放”,并抛出下一行:io.netty.util.IllegalReferenceCountException:refCnt:0

但是,如果我在列表中的下一个缓冲区尝试它:

    ByteBuf bb2 = BUFFERS.get(1);
    bb2.writeBytes(headerPrefix); // headerPrefix = 4 bytes array

效果很好。

    ByteBuf bb3 = BUFFERS.get(2);
    bb3.writeBytes(headerPrefix); // headerPrefix = 4 bytes array

失败与bb1相同。

当你说:

每个数据源使用少量可重用缓冲区的方法,应该可以节省分配直接缓冲区的时间

,这正是PooledByteBufAllocator.DEFAULT 之所以要缓冲缓冲区,部分原因是为了避免为直接ByteBuf分配较长的时间。

因此,通过从池分配器分配一系列ByteBuf,您将永久保留应该共享的池资源。

如果您打算继续使用该方法,那么我将使用directBuffer(int)从默认的UnpooledByteBufAllocator中预先分配您的缓冲区

您还在netty问题跟踪器中打开了一个错误,我在其中解释说我认为该错误按Java文档中的说明运行。 我认为您是对的,可能有点令人惊讶且难以推理,但它再次与javadocs完全匹配。

就像问题中提到的那样,我们已弃用此方法,并引入了一种不会带来这种“令人惊讶的行为”的替代方法。 这将是4.1.26.Final的一部分。

参见https://github.com/netty/netty/pull/8040https://github.com/netty/netty/pull/8096

经过进一步测试后,似乎出现了此问题。 分配了最初的3个缓冲区后,然后使用:

 Unpooled.wrappedBuffer(BUFFERS.get(0), BUFFERS.get(2));
 System.out.println(bb1.refCnt()); // returns 0  !!!

并且任何基础/引用的缓冲区(例如0和2)为空-尚未写入任何数据,通过对wrapdBuffer()方法的调用可以释放空缓冲区。 空缓冲区上的refcount()返回0。

另一方面,如果在调用wrappedBuffer()方法之前将一些数据放入引用的缓冲区中,则该缓冲区不会释放,并且该缓冲区上的refcount()返回1。

 HEADER_BUFFERS.get(0).writeBytes(headerPrefix);
 Unpooled.wrappedBuffer(BUFFERS.get(0), BUFFERS.get(2));
 System.out.println(bb1.refCnt()); // returns 1  !!!

也许这是自动释放未使用的内存的功能,但是在这种情况下,它表现为自身而不是错误。 希望这是Netty设计师/业内人士可以为我们解决的问题。

进一步的研究提供了围绕/解决方案-使用.retain()的方法:

Unpooled.wrappedBuffer(BUFFERS.get(0).retain(), BUFFERS.get(2).retain());

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM