添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接


byte数组 ios json byte数组添加数据_池化

ByteBuffer与ByteBuf通常用于字节数据的操作,比如对网络IO Channel进行读取或者写入,其中封装了一些操作byte数组的方法,还是很实用的。ByteBuf是对ByteBuffer的封装,由Netty提供,提供了更方便、更丰富的byte数组功能。

ByteBuffer

ByteBuffer的几个基本属性:

  • position:表示进行下一个读写操作的下标位置
  • limit:表示进行读写操作时的结束位置;
  • capacity:表示存储的容量
  • mark: 对数据进行标记

初始化 :对ByteBuffer进行初始化,可以使用静态方法 wrap(byte[] data) 封装数组,也可以通过另一个静态方法 allocate(int size) 初始化指定长度的ByteBuffer。

初始状态 position:0,limit:值为最大长度,capacity:值为最大长度

byte数组 ios json byte数组添加数据_java_02

数据写入(或读取) :每写入(或读取)一个值,position加一(图中是写入两个数据之后的位置)。

byte数组 ios json byte数组添加数据_数据_03

准备读取(或写入) :使用 flip() 方法翻转准备数据读取(或写入),进行读取(或写入)时,不能超过limit限制,读超出限制报错 BufferUnderflowException (写超出限制报错 BufferOverflowException

byte数组 ios json byte数组添加数据_byte数组 ios json_04

清除数据 :回到初始状态可以调用 clear() 方法,但是数据并不会删除,当写入时会直接覆盖对应位置的值。

byte数组 ios json byte数组添加数据_零拷贝_05

标记位置 :当需要进行标记时,可以使用 mark() 方法,即 mark=position ;进行读取后,可调用 reset() 方法直接回到mark标记的位置,即 position=mark

ByteBuf

相比于ByteBuffer,ByteBuf对其提升主要体现在以下几个方面:

  1. 读和写的下标索引采用了不同的两个值进行操作,读写模式切换无需进行 flip 操作
  2. 支持堆内存和直接内存的池化以及零拷贝
  3. 可以按照需要进行容量的扩展
  4. 支持方法的链式调用

ByteBuf的读写

下面一个ByteBuf数字的内部结构,其中会有一个readIndex和writeIndex索引来记录读取和写入的位置。当你从ByteBuf 读取时,它的readerIndex将会被递增已经被读取的字节数。同样地,当你写入ByteBuf时,它的writerIndex也会被递增。读取超出writerIndex会触发 IndexOutOfBoundsException 。在所有方法中,名称以read或者write开头的方法,将会推进其对应的索引,而名称以set或者get为开头的操作则不会。

byte数组 ios json byte数组添加数据_java_06

对于已经读取过不需要的字段,可以通过 discardReadBytes() 方法进行回收,它会把可读字节复制到字节数组的前面,回收过的ByteBuf会变成下图的样子。

byte数组 ios json byte数组添加数据_数据_07

零拷贝

对于一般的网络IO读写,需要到端口的缓冲区进行读取之后,切换内核态写入,然后用户程序从用户态切换为内核态,拷贝数据到用户内存中,才能进行数据的处理。这里面需要几次的上下文切换拷贝数据。而对于零拷贝,则是直接访问相应位置的系统内存,节省了多次上下文切换拷贝的开销,大大提高了数据IO的读写效率。

ByteBuf的分配

ByteBuf分配主要有两种方式:池化与非池化。池化操作的内存不需要自己释放内存,它会自己回收复用,通常用于长时间存储的数据。而非池化操作内存则适用于临时存储的数据,一般用于低延迟、高性能场景。对于缓冲区的内存分配的位置主要有堆内存和直接内存两种,堆内存用的是JVM中堆的内存位置,直接内存用的是系统内存(堆外内存),直接内存由于零拷贝的方式读写数据效率更高,但是容易造成系统内存不足,需要用完立即回收。

池化分配 对于池化的分配,我们可以使用PooledByteBufAllocator进行创建。

ByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
ByteBuf buf = allocator.buffer(512); 
复制代码

也使用ByteBufAllocator类创建一个池化的ByteBuf。

ByteBufAllocator allocator = new ByteBufAllocator() { 
    @Override public ByteBuf buffer() { 
        return PooledByteBufAllocator.DEFAULT.buffer(); 
    @Override public ByteBuf buffer(int initialCapacity) { 
        return PooledByteBufAllocator.DEFAULT.buffer(initialCapacity);
ByteBuf buf = allocator.buffer(512);
复制代码

ByteBufAllocator类主要有以下几种方法:

  • buffer() :创建一个基于堆的缓冲区
  • derectBuffer() :创建一个基于直接内存的缓冲区
  • compositeBuffer() :创建一个由堆内存或直接内存的复合缓冲区
  • ioBuffer() :创建一个用于套接字的I/O操作的ByteBuf

非池化分配 对于非池化的分配,可以采用Unpooled的工具类,它提供了一些静态方法来创建未池化的 ByteBuf实例。

ByteBuf buf = Unpooled.buffer(512);
复制代码

Unpooled类的方法主要有以下几种方法:

  • buffer() :创建一个基于堆的缓冲区
  • derectBuffer() :创建一个基于直接内存的缓冲区
  • wrappedBuffer() :返回一个包装了给定数据的ByteBuf
  • copiedBuffer() :返回一个复制了给定数据的 ByteBuf

派生缓冲区

当需要使用多个视图去操作内存时,可以使用以下方法获取:

  • duplicate()
  • slice()
  • Unpooled.unmodifiableBuffer()
  • order()
  • readSlice()

这几个方法可以返回一个具有单独的读索引、写索引和标记索引实例,但是实际数据是共享的。如果需要复制一个全新的缓冲区对象,可以使用 copy() 方法。

其他可能用到的操作

ByteBufUtil类 :ByteBufUtil提供了用于操作ByteBuf的一些方法。包括 hexdump() equals()

release() :进行内存回收。

readableBytes() :返回可读取的字节数

writeableBytes() :返回可写入的字节数

isReable() :是否至少有一个字节可读取

isWriteable :是否至少有一个字节可写入

array() :返回一个字节数组