zoukankan      html  css  js  c++  java
  • Netty源码分析之ByteBuf(一)—ByteBuf中API及类型概述

    ByteBuf是Netty中主要的数据容器与操作工具,也是Netty内存管理优化的具体实现,本章我们先从整体上对ByteBuf进行一个概述;

    AbstractByteBuf是整个ByteBuf的框架类,定义了各种重要的标志位与API供具体的实现类使用与实现;下面我们就从AbstractByteBuf类入手对ByteBuf的读写机制与API进行一个简单的介绍

        private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractByteBuf.class);
        private static final String LEGACY_PROP_CHECK_ACCESSIBLE = "io.netty.buffer.bytebuf.checkAccessible";
        private static final String PROP_CHECK_ACCESSIBLE = "io.netty.buffer.checkAccessible";
        static final boolean checkAccessible; // accessed from CompositeByteBuf
        private static final String PROP_CHECK_BOUNDS = "io.netty.buffer.checkBounds";
        private static final boolean checkBounds;
    
        static {
            if (SystemPropertyUtil.contains(PROP_CHECK_ACCESSIBLE)) {
                checkAccessible = SystemPropertyUtil.getBoolean(PROP_CHECK_ACCESSIBLE, true);
            } else {
                checkAccessible = SystemPropertyUtil.getBoolean(LEGACY_PROP_CHECK_ACCESSIBLE, true);
            }
            checkBounds = SystemPropertyUtil.getBoolean(PROP_CHECK_BOUNDS, true);
            if (logger.isDebugEnabled()) {
                logger.debug("-D{}: {}", PROP_CHECK_ACCESSIBLE, checkAccessible);
                logger.debug("-D{}: {}", PROP_CHECK_BOUNDS, checkBounds);
            }
        }
    
        static final ResourceLeakDetector<ByteBuf> leakDetector =
                ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ByteBuf.class);
    
        int readerIndex;
        int writerIndex;
        private int markedReaderIndex;
        private int markedWriterIndex;
        private int maxCapacity;

    在上面的代码中,我们需要知道ByteBu维护了两个不同的索引readerIndex与writerIndex,这两个索引默认都是从0开始,一个用于读取,一个用于写入。

     

    当你从ByteBuf读取数据时,readerIndex会递增已经读取的字节数,同理当你写入数据时,writerIndex也会随之递增。可以说ByteBuf中各种读写API都是基于readerIndex与writerIndex来控制的。从索引操作上,ByteBuf中API基本分为两大部分,会引发索引值递增的read(读)和write(写操作),反之不会引发索引值递增的get或set操作。下面我们看下ByteBu中常用API的参考说明

    ByteBuf API

    read操作

    readBoolean() 返回当前readIndex的Boolean值,readIndex增加1
    readByte(int) 返回当前readIndex处的字节值,readIndex增加1
    readUnsignedByte() 返回当前readIndex处的无符号字节值,readIndex增加1
    readInt() 返回当前readIndex处的int值,readIndex增加4
    readUnsignedInt() 返回当前readIndex处的无符号int值,返回类型为long,readIndex增加4
    readLong() 返回当前readIndex处的long值,readIndex增加8
    readShort()

    返回当前readIndex处的short值,readIndex增加2

    readUnsignedShort() 返回当前readIndex处的short值,readIndex增加2

     wirte操作

    writeBoolean() 在当前writerIndex处写入一个Boolean值(1或0),writerIndex增加1
    writeByte(int) 在当前writerIndex处写入一个byte值,writerIndex增加1
    writeShort(int) 在当前writerIndex处写入一个short值,writerIndex增加2
    writeInt(int) 在当前writerIndex处写入一个int值,writerIndex增加4
    writeLong(int) 在当前writerIndex处写入一个long值,writerIndex增加4

     

     get操作

    getBoolean(int) 返回给的索引处的Boolean值,readIndex值不变
    getByte(int) 返回给的索引处的Byte值,readIndex值不变
    getShort(int) 返回给的索引处的short值,readIndex值不变
    getInt(int) 返回给的索引处的int值,readIndex值不变
    getLong(int) 返回给的索引处的long值,readIndex值不变

     

     set操作

    setBoolean(int,boolean) 在指定索引处设置一个Boolean值(1或0),writerIndex值不变
    setByte(int,int) 在指定索引处设置一个byte值,writerIndex值不变
    setShort(int,int) 在指定索引处设置一个short值,writerIndex值不变
    setInt(int,int) 在指定索引处设置一个int值,writerIndex值不变
    setLong(int,int) 在指定索引处设置一个long值,writerIndex值不变

    ByteBuf类型

    Netty中ByteBuf相关实现类的UML图

     Netty在构建ByteBuf时,有以下多种分类:

    1、从内存类型上,分为堆内存与直接内存,HeapByteBuf与DirectByteBuf

    我们知道Java中大部分对象都是在堆内存中存储,由jvm统一管理,但NIO中 的 ByteBuffer 类允许 JVM 实现通过本地调用来分配内存,Netty中对基于直接内存的ByteBuffer也进行了封装,这主要是为了避免在每次调用本地 I/O 操作之前(或者之后)将缓冲区的内容复 制到一个中间缓冲区(或者从中间缓冲区把内容复制到缓冲区),直接缓冲区不会占用堆的容量。事实上,在通过套接字发送它之前,JVM将会在内部把你的缓冲 区复制到一个直接缓冲区中。所以如果使用直接缓冲区可以节约一次拷贝,提高IO操作性能。使用直接内存的优缺点如下:

    (1)优点:由于数据直接在内存中,不存在从JVM拷贝数据到直接缓冲区的过程,提高IO操作性能。

    (2)缺点:相对于基于堆的缓冲区,它们的分配和释放都较为昂贵,同时由于直接内存不受JVM垃圾回收统一管理,需要自己手动回收,需要特别注意内存泄露的问题。

    2、从分配模式上,分为池化与非池化;PooledByteBuf与UnpooledByteBuf

    对于频繁的申请与释放内存带来的性能损耗与碎片化问题,Netty基于池化思想通过预先申请一块专用内存地址作为内存池进行管理,从而不需要每次都进行分配和释放。

    从上面的UML图中可以看到,基于AbstractByteBuf父类针对直接内存与堆内存,也都有其对应的池化与非池化实现类;

    PooledByteBuf  下有  PooledUnsafeDirectByteBuf、PooledHeapByteBuf、PooledDirectByteBuf三个子类实现

    UnpooledByteBuf 下则是 UnpooledDirectByteBuf及其子类UnpooledUnsafeDirectByteBuf 与 UnpooledHeapByteBuf及其子类UnpooledUnsafeHeapByteBuf

    3、从具体的操作类上,分为Unsafe与非Unsafe

     从上面的子类实现中,我们发现每种分类中又包含Usafe与非Unsafe的区别,我们知道java可以通过Unsafe类直接操作内存区域,所以这些类的区别就是在于是调用jdk的Unsafe直接去操作对象的内存地址还是通过jdk封装的安全方式操作内存。

    我们通过PooledByteBuf下Unsafe与非Unsafe实现类的getByte方法,看下具体的区别

    PooledUnsafeHeapByteBuf

        @Override
        protected byte _getByte(int index) {
            return UnsafeByteBufUtil.getByte(addr(index));
        }

    Netty中封装了一个UnsafeByteBufUtil类,进入内部实现看到调用的是Unsafe对象进行具体操作

        static byte getByte(long address) {
            return UNSAFE.getByte(address);
        }


    PooledHeapByteBuf

    跟踪其内部代码则可以看到字节是基于数组操作的

        @Override
        protected byte _getByte(int index) {
            return HeapByteBufUtil.getByte(memory, idx(index));
        }
    
        static byte getByte(byte[] memory, int index) {
            return memory[index];
        }

    PooledDirectByteBuf

    针对DirectByteBuf大家需要灵活理解,PooledUnsafeDirectByteBuf 与PooledUnsafeDirectByteBuf的区别一个是UnsafeByteBufUtil类直接操作,一个是使用java NIO中的DirectByteBuf类进行操作,但因为要操作直接内存,最后还都是要基于jdk的unsafe类实现;

    对比他们两种实现,可以看出Unsafe与非Unsafe的区别,前者是通过jdk的Unsafe类去操作数据,后者直接通过数组或者jdk底层的DirectByteBuf去操作数据。

    ByteBuf作为Netty中数据操作与内存分配的具体实现,是Netty中最为底层的也是最精细的一部分,本文只是对ByteBuf的一个简单概述,后续我们会进一步对其进行探索与剖析,更好的展示Netty内部具体实现,希望对大家能有所帮助,其中如有不足与不正确的地方还望指出与海涵。

    关注微信公众号,查看更多技术文章。

     

    转载说明:未经授权不得转载,授权后务必注明来源(注明:来源于公众号:架构空间, 作者:大凡)

  • 相关阅读:
    Linux设备驱动之Ioctl控制
    虚拟内存与物理内存的区别
    怎么远程控制他人电脑
    二维数组和指针
    二维数组和指针(C语言)
    帧率、码流与分辨率相关知识
    深入理解FIFO
    安装lsb_release
    Linux初学之vmware Workstation 网络连接三种模式
    RTSP协议学习笔记
  • 原文地址:https://www.cnblogs.com/dafanjoy/p/13530708.html
Copyright © 2011-2022 走看看