• Netty学习五:Buffers


    1. Netty中的缓冲

    在Netty中并没有使用Java自带的ByteBuffer,而是自己实现提供了一个缓存区来用于标识一个字节序列,并帮助用户操作原始字节或者自定义的POJO。
    

    Java NIO的ByteBuffer问题

    • 长度固定,不能动态扩展和收缩
    • 只有一个标识位置的指针,读写时要手动调用相关方法
    • API功能有限,需要自己实现更高级的功能

    如上图,channel与对端的I/O读写都要操作Buffers。当有读操作时,把数据从内核区读取到用户区,当有写操作时,把数据从用户区写到内核区。
    

    2. ByteBuf

    ByteBuf是Netty的实现的最基本的数据缓冲,它包括Heap Buffer和Direct Buffer。
    
    ByteBuf实现了高级的功能和API,是Java NIO ByteBuffer更高级的封装和实现。
    

    2.1 ByteBuf结构

    如上图是ByteBuf的结构,其中:

    • readerIndex:读指针,最开始时等于0
    • writerIndex:写指针,最开始时等于0
    • capacity:代表这块内存区域的大小

    2.2 写入数据

    当写入N个数据后,writerIndex向后移动,但要保证writerIndex<capacity,这时可读数据为writerIndex,可写数据长度为capacity-writerIndex
    

    2.3 读取数据

    每读一个数据,readerIndex向后移动,但readerIndex<=writerIndex,可读数据writerIndex-readerIndex这时readerIndex-0则是可丢弃的数据段
    

    2.4 动态扩展

    如果数据继续写入缓冲区,而缓存区的writable区间已经被判断空间不足,那就有两个方法可以解决:
    
    • 动态扩展缓冲区,由ByteBuf实现

      重新创建一个新的ByteBuffer,并将之前的ByteBuffer复制到新创建的ByteBuffer中,最后释放老的ByteBuffer

    • 重用discard 区域,需要调用discardReadBytes方法

    2.5 Heap Buffer

    Heap Buffer:堆内存字节缓存区,特点是内存的分配和回收速度快,但在I/O读写时需要额外一次内存复制,将堆内存对应的缓冲区复制到内核Channel中。
    

    2.6 Direct Buffer

    Direct Buffer:直接内存字节缓存区,直接从Socket Channel中读写数据,少了一次内存复制,但内存的分配和回收速度慢一些。
    

    2.7 Composite Buffer

    Composite Buffer:复合缓冲区,我们可以创建多个不同的ByteBuf,然后提供一个这些ByteBuf 组合的视图。复合缓冲区就像一个列表,我们可以动态的添加和删除其中的ByteBuf
    

    2.8 引用计数

    ByteBuf扩展了ReferenceCountered接口,这个接口定义的功能主要是引用计数。
    通过refCnt的数值来计算ByteBuf,如果refCnt等于1了,调用deallocate来回收ByteBuf,对于池化的ByteBuf则放入内存池,其他则释放内存。
    

    3. 缓冲对象池

    Netty使用Recycler来实现轻量级缓冲对象池。
    

    3.1 Recycler

    • Handle

      定义接口

    • Stack

      Stack具体维护着对象池数据,向Recycler提供push和pop两个主要访问接口,pop用于从内部弹出一个可被重复使用的对象,push用于回收以后可以重复使用的对象

    • elements

      对象池数组

    3.1.1 get

    • 首先获得当前线程关联的stack
    • 从stack中pop出一个handle
    • 如果没有则k新建一个
    • 从handle中取出对象

    3.1.2 recycle

    • 对象通过引用计数来管理,如果发现引用计数等于1了,就通知Recycler回收对象
    • 将对象放入stack中
    • 如果超过当前池的大小但还没达到最大值,则新建池,并拷贝对象

    3.2 内存池总结

    Netty中每个线程都关联着一个内存对象池,用来操作缓冲对象

    Netty中的I/O操作都需要涉及缓冲区,如果不使用内存池技术,系统将频繁的创建和回收buffer,对buffer的管理将十分困难。对于HeapBuf和Directbuf都分配和释放频率,并且对于Directbuf弥补一定的分配回收代价。
    

    4. Zero-Copy

    • Netty 提供了 CompositeByteBuf 类, 它可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝

    • 通过 wrap 操作, 我们可以将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操作

    • ByteBuf 支持 slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝

    • 通过 channel的tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题.

  • 相关阅读:
    OpenCV开发笔记(五十八):红胖子8分钟带你深入了解图像的矩(图文并茂+浅显易懂+程序源码)
    Oracle数据迁移后由列的直方图统计信息引起的执行计划异常
    oracle统计信息的锁定与解锁
    [统计信息系列7] Oracle 11g的自动统计信息收集
    [统计信息系列6] 数据字典统计信息
    [统计信息系列5] 系统统计信息
    MongoDB创建与删除集合(collection)
    MongoDB创建与删除数据库
    [统计信息系列4] 列的统计信息
    [统计信息系列3] 索引的统计信息
  • 原文地址:https://www.cnblogs.com/TomSnail/p/6218163.html
Copyright © 2020-2023  润新知