Buffer

Buffer概念

Buffer是针对原始数据类型具有固定大小的数据块容器,从通道读取数据或向通道写入数据,可以看作通道读写端点。

它提供了一系列方法可以方便的处理内存块,以便从通道读取和写入数据。

Buffer分类

Java为除了布尔型以为的其它原始类型提供了对应的Buffer实现。如下图所示:
Buffer

Buffer的重要属性

  • Capacity: Buffer所能容纳的最大数据量/字节,不能被修改。一旦缓冲区已满,应在写入前清理。
  • Limit: 读写操作限额。写模式下表示最大支持的写入量,最大不能超过Capacity;读模式下表示可以读取的最大数据量。
  • Position: 要操作数据的起始位置,可以被get和put方法自动更新。
  • Mark: 标记buffer的位置。调用mark()记录当前位置;调用reset()回退到记录位置。

Buffer的主要方法

  • allocate(int capacity)
    创建新的Buffer并指定最大容量。
  • read() and put()
    通道的read方法用于将数据从通道写入缓冲区,而put是缓冲区的方法,用于将数据写入缓冲区
  • flip()
    将Buffer从写模式切换为读模式,设置Position为0,并将Limit设置为写入时的位置
  • write() and get()
    通道的write方法用于将数据从缓冲区写入通道,而get方法是缓冲区的方法,用于从缓冲区读取数据
  • rewind()
    用于重新读取数据,设置Postion为0,但不会改变Limit
  • clear() and compact()
    两个方法都是用来将Buffer读模式切换为写模式。clear()使位置归0,Limit等于Capacity,但不会清除Buffer中的数据,只是标记为被重新初始化;当存在未读数据时,使用compact()方法,会将未读数据复制到开头,并设置位置为最后一个未读数据位置之后,Limit仍然设置为Capacity
  • mark() and reset()
    mark()标记当前位置,reset()退回到标记位置

两种特别的Buffer

  • Direct Buffer:Buffer中定义了isDirect(),用于区分堆内Buffer和堆外Buffer。Direct Buffer属于堆外内存,使用allocateDirect方法直接创建,而堆内使用allocate方法创建。
  • MappedByteBuffer: 将文件按照指定大小直接映射为内存区域,当程序访问这个内存区域时将直接操作这块儿文件数据,省去了空间内存拷贝。用FileChannel.map创建MappedByteBuffer,其本质属于Direct Buffer。

Buffer和垃圾收集

Java会尽量的对DirectBuffer仅做本地IO操作,对大数据量IO密集型操作,可能会带来非常大的性能优势。原因如下:

  • Direct Buffer整个生命周期中内存地址不会发生变化,内核可以安全的访问,很多IO操作会很高效
  • 减少堆内对象存储的可能额外维护工作,访问效率有所提高

Direct Buffer创建和销毁过程中,都会比一般的堆内Buffer增加部分开销,所以通常都建议用于长期使用、数据较大的场景。

另外,大多数垃圾收集过程中,都不会主动收集Direct Buffer,它的垃圾收集过程,就是基于Cleaner(一个内部实现)和幻象引用(PhantomReference)机制,其本身不是public类型,内部实现了一个Deallocator负责销毁的逻辑。对它的销毁往往要拖到full GC的时候,所以使用不当很容易导致OutOfMemoryError。

DirectBuffer回收建议:

  • 在应用程序中,显式地调用System.gc()来强制触发。
  • 在大量使用Direct Buffer的部分框架(Netty)中,框架会自己在程序中调用释放方法。
  • 重复使用Direct Buffer。
版权声明:玥玥 发表于 2021-06-10 11:45:55。
转载请注明:Buffer | 女黑客导航