netty(十三)初识Netty - ByteBuf 拷贝 及

作者: 我犟不过你 | 来源:发表于2021-11-12 15:16 被阅读0次

    Netty基于不同的使用场景,提供了几个ByteBuf当中零拷贝的方法。这些方法和我们在NIO当中谈到的不同,在NIO当中的零拷贝最终是为了减少用户态和内核态之间的数据拷贝。

    本章节我们针对Netty提供的几个方法进行简单学习和使用。

    一、零拷贝

    先声明一点,如果要使用以下方法的话,可能要配合前面文章介绍的retain()方法去增加引用计数,否则原ByteBuf被release()后,则会导致我们拷贝出来的buf使用失败,导致异常。

    1.1 slice

    “零拷贝”的体现之一,对原始 ByteBuf 进行切片,切分成多个 ByteBuf,切片后的 ByteBuf 并没有发生内存复制,只是多了引用到分片后的Bytebuf,然而还是使用的原始 ByteBuf 的内存,切片后的 ByteBuf 维护独立的 read,write 指针,修改子分片,会修改原ByteBuf。

    示例代码:

        public static void main(String[] args) {
            ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(10);
            byteBuf.writeBytes(new byte[]{1,2,3,4,5,6,7,8,9,0});
    
            printBuf(byteBuf);
    
            //分片1
            ByteBuf slice1 = byteBuf.slice(0, 5);
            printBuf(slice1);
    
            //分片2
            ByteBuf slice2 = byteBuf.slice(5, 5);
            printBuf(slice2);
    
            //将最后一位0修改成10
            slice2.setByte(4,10);
            printBuf(slice2);
    
            //打印修改后的byteBuf
            printBuf(byteBuf);
    
        }
    
        static void printBuf(ByteBuf byteBuf){
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i< byteBuf.writerIndex();i++) {
                stringBuilder.append(byteBuf.getByte(i));
            }
            System.out.println(stringBuilder);
        }
    

    结果:

    1234567890
    12345
    67890
    678910
    12345678910
    

    注意:slice后的分片,不能再次写入新的数据,这回影响原ByteBuf。

    1.2、duplicate

    “零拷贝”的体现之一,拷贝了原始 ByteBuf 所有内容,长度仍然以byteBuf为准,不能写入新数据,也是与原始 ByteBuf 使用同一块底层内存,只是读写指针是独立的。

    使用示例:

    public class DuplicateTest {
    
        public static void main(String[] args) {
            ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(10);
            byteBuf.writeBytes(new byte[]{1,2,3,4,5,6,7,8,9,0});
    
            //拷贝一块buf
            ByteBuf duplicate = byteBuf.duplicate();
            printBuf(duplicate);
    
            //将最后一位0修改成10,看一下byteBuf
            duplicate.setByte(9,10);
            printBuf(byteBuf);
    
            // 写入新数据11,看byteBuf
            duplicate.writeByte(11);
            printBuf(byteBuf);
        }
    
        static void printBuf(ByteBuf byteBuf){
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i< byteBuf.writerIndex();i++) {
                stringBuilder.append(byteBuf.getByte(i));
            }
            System.out.println(stringBuilder);
        }
    }
    

    1.3 CompositeByteBuf

    “零拷贝”的体现之一,可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf,避免拷贝。

    public class CompositeByteBufTest {
    
        public static void main(String[] args) {
            ByteBuf byteBuf1 = ByteBufAllocator.DEFAULT.buffer(5);
            byteBuf1.writeBytes(new byte[]{1, 2, 3, 4, 5});
    
            ByteBuf byteBuf2 = ByteBufAllocator.DEFAULT.buffer(5);
            byteBuf2.writeBytes(new byte[]{6, 7, 8, 9, 0});
    
            CompositeByteBuf compositeByteBuf = ByteBufAllocator.DEFAULT.compositeBuffer();
            // 组合两个byteBuf,主要要使用带有increaseWriteIndex的,否则会失败。
            compositeByteBuf.addComponents(true,byteBuf1, byteBuf2);
    
            printBuf(compositeByteBuf);
        }
    
        static void printBuf(ByteBuf byteBuf){
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i< byteBuf.writerIndex();i++) {
                stringBuilder.append(byteBuf.getByte(i));
            }
            System.out.println(stringBuilder);
        }
    }
    

    1.4 Unpooled

    Unpooled 是一个工具类,提供了非池化的 ByteBuf 创建、组合、复制等操作。

    这里仅介绍其跟“零拷贝”相关的 wrappedBuffer 方法

    使用示例:

        public static void main(String[] args) {
            ByteBuf byteBuf1 = ByteBufAllocator.DEFAULT.buffer(5);
            byteBuf1.writeBytes(new byte[]{1, 2, 3, 4, 5});
    
            ByteBuf byteBuf2 = ByteBufAllocator.DEFAULT.buffer(5);
            byteBuf2.writeBytes(new byte[]{6, 7, 8, 9, 0});
    
    //        CompositeByteBuf compositeByteBuf = ByteBufAllocator.DEFAULT.compositeBuffer();
    //        // 组合两个byteBuf,主要要使用带有increaseWriteIndex的,否则会失败。
    //        compositeByteBuf.addComponents(true,byteBuf1, byteBuf2);
    
            // 组合两个byteBuf,底层使用CompositeByteBuf。
            ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(byteBuf1, byteBuf2);
    
            printBuf(wrappedBuffer);
        }
    
        static void printBuf(ByteBuf byteBuf){
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i< byteBuf.writerIndex();i++) {
                stringBuilder.append(byteBuf.getByte(i));
            }
            System.out.println(stringBuilder);
        }
    

    二、深度拷贝

    ByteBuf提供了copy方法,这一类方法是真正的拷贝原ByteBuf到新的内存,返回一个新的ByteBuf,与原ByteBuf没有关系。

    提供两个拷贝,一个是全量;一个指定位置和长度。

    public abstract ByteBuf copy();
    
    public abstract ByteBuf copy(int index, int length);
    

    使用示例:

    public class CopyTest {
    
        public static void main(String[] args) {
            ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(10);
            byteBuf.writeBytes(new byte[]{1,2,3,4,5,6,7,8,9,0});
    
            ByteBuf copy1 = byteBuf.copy();
            printBuf(copy1);
    
            ByteBuf copy2 = byteBuf.copy(5, 5);
            printBuf(copy2);
        }
    
        static void printBuf(ByteBuf byteBuf){
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i< byteBuf.writerIndex();i++) {
                stringBuilder.append(byteBuf.getByte(i));
            }
            System.out.println(stringBuilder);
        }
    }
    

    关于Bytebuf的入门就介绍这么多了,后面会深入去探讨更细节的内容。

    Bytebuf简单总结

    • 池化 - 可以重用池中 ByteBuf 实例,更节约内存,减少内存溢出的可能
    • 读写指针分离,不需要像 ByteBuffer 一样切换读写模式
    • 可以自动扩容
    • 支持链式调用,使用更流畅
    • 很多地方体现零拷贝,例如 slice、duplicate、CompositeByteBuf

    相关文章

      网友评论

        本文标题:netty(十三)初识Netty - ByteBuf 拷贝 及

        本文链接:https://www.haomeiwen.com/subject/jvfozltx.html