Netty学习--ByteBuf

Netty使用ByteBuf替换Java NIO提供的ByteBuffer(使用过于复杂).

ByteBuf 的API

下面是一些ByteBuf API 的优点:

  • 它可以被用户自定义的缓冲区类型扩展;
  • 通过内置的复合缓冲区类型实现了透明的零拷贝;
  • 容量可以按需增长(类似于JDK 的StringBuilder);
  • 在读和写这两种模式之间切换不需要调用ByteBuffer 的flip()方法;
  • 读和写使用了不同的索引;
  • 支持方法的链式调用;
  • 支持引用计数;
  • 支持池化。
ByteBuf如何工作的
一个读索引和写索引都设置为0 的16 字节ByteBuf

名称以read 或者write 开头的ByteBuf 方法,将会推进其对应的索引,而名称以set 或者get 开头的操作则不会

ByteBuf 的使用模式
  • 堆缓冲区
    最常用的ByteBuf 模式是将数据存储在JVM 的堆空间中。这种模式被称为支撑数组(backing array),它能在没有使用池化的情况下提供快速的分配和释放。
ByteBuf heapBuf = ...;
if (heapBuf.hasArray()) { // 检查ByteBuf 是否有一个支撑数组
byte[] array = heapBuf.array(); // 如果有,则获取对该数组的引用
int offset = heapBuf.arrayOffset() + heapBuf.readerIndex(); // 计算第一个字节的偏移量。
int length = heapBuf.readableBytes(); // 获得可读字节数
handleArray(array, offset, length); // 使用数组、偏移量和长度作为参数调用你的方法
}
  • 直接缓冲区
    直接缓冲区的内容将驻留在常规的会被垃圾回收的堆之外.
ByteBuf directBuf = ...;
if (!directBuf.hasArray()) { // 检查ByteBuf 是否由数组支撑。如果不是,则这是一个直接缓冲区
int length = directBuf.readableBytes(); // 获取可读字节数
byte[] array = new byte[length]; // 分配一个新的数组来保存具有该长度的字节数据
directBuf.getBytes(directBuf.readerIndex(), array); // 将字节复制到该数组
handleArray(array, 0, length); // 使用数组、偏移量和长度作为参数调用你的方法
}
  • 复合缓冲区
    为多个ByteBuf 提供一个聚合视图。在这里你可以根据需要添加或者删除ByteBuf 实例.子类CompositeByteBuf实现.
持有一个头部和主体的CompositeByteBuf
字节级操作
  • 随机访问索引
    ByteBuf 的索引是从零开始的:第一个字节的索引是0,最后一个字节的索引总是capacity() - 1。
  • 顺序访问索引
    两个索引划分成3 个区域.
ByteBuf 的内部分段
  • 可丢弃字节
    可丢弃字节的分段包含了已经被读过的字节。通过调用discardRead-Bytes()方法,可以丢弃它们并回收空间
丢弃已读字节之后的ByteBuf
  • 可读字节
    任何名称以read 或者skip 开头的操作都将检索或者跳过位于当前readerIndex 的数据,并且将它增加已读字节数。
  • 可写字节
    任何名称以write 开头的操作都将从当前的writerIndex 处开始写数据,并将它增加已经写入的字节数
  • 索引管理
    通过调用markReaderIndex()、markWriterIndex()、resetWriterIndex()和resetReaderIndex()来标记和重置ByteBuf 的readerIndex 和writerIndex。可以通过调用clear()方法来将readerIndex 和writerIndex 都设置为0.
clear()方法被调用之前
在clear()方法被调用之后

注意,clear()方法并不会清除内存中的内容。

  • 查找操作
    boolean process(byte value) 检查输入值是否是正在查找的值
    forEach Byte(ByteBufProcessor.FIND_NUL) 查找null
  • 派生缓冲区

1.duplicate();
2.slice();
3.slice(int, int);
4.Unpooled.unmodifiableBuffer(…);
5.order(ByteOrder);
6.readSlice(int)。

返回新的ByteBuf 实例,如果你修改了它的内容,也同时修改了其对应的源实例,所以要小心。如果需要一个现有缓冲区的真实副本,请使用copy()或者copy(int, int)

  • 读/写操作
    有两种类别的读/写操作:

get()和set()操作,从给定的索引开始,并且保持索引不变;
read()和write()操作,从给定的索引开始,并且会根据已经访问过的字节数对索引进行调整。

ByteBufHolder 接口

content() 返回由这个ByteBufHolder 所持有的ByteBuf
copy() 返回这个ByteBufHolder 的一个深拷贝,包括一个其所包含的ByteBuf 的非共享拷贝
duplicate() 返回这个ByteBufHolder 的一个浅拷贝,包括一个其所包含的ByteBuf 的共享拷贝

ByteBuf 分配
  • 按需分配:ByteBufAllocator 接口
    Netty 通过interface ByteBufAllocator 实现了(ByteBuf 的)池化
    PooledByteBufAllocator和Unpooled-ByteBufAllocator。前者池化了ByteBuf的实例以提高性能并最大限度地减少内存碎片(一种称为jemalloc的已被大量现代操作系统所采用的高效方法来分配内存).后者的实现不池化ByteBuf实例,并且在每次它被调用时都会返回一个新的实例.
  • Unpooled 缓冲区

buffer()
buffer(int initialCapacity)
buffer(int initialCapacity, int maxCapacity)
返回一个未池化的基于堆内存存储的
ByteBuf
directBuffer()
directBuffer(int initialCapacity)
directBuffer(int initialCapacity, int maxCapacity)
返回一个未池化的基于直接内存存储
的ByteBuf
wrappedBuffer() 返回一个包装了给定数据的ByteBuf
copiedBuffer() 返回一个复制了给定数据的ByteBuf

  • ByteBufUtil 类
    hexdump() 以十六进制的表示形式打印ByteBuf 的内容
    equals(ByteBuf, ByteBuf) 它被用来判断两个ByteBuf实例的相等性
  • 引用计数
    引用计数对于池化实现(如PooledByteBufAllocator)来说是至关重要的,它降低了内存分配的开销。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容