Java NIO之Buffers

Buffer

Buffer到底是什么?从JDK源码文档中,可以一窥究竟:

A container for data of a specific primitive type.

Buffer是某种基本类型数据的容器。

A buffer is a linear, finite sequence of elements of a specific primitive type.

Buffer是某种基本类型元素的线性有限序列。

关键字:线性容器、容量固定、元素必须是基本数据类型。(这不就是数组么!)

在NIO语境中,Buffer是这样的:

A buffer is an object that stores a fixed amount of data to be sent to or received from an I/O service .

简单点,Buffer是应用程序和Channels读写数据的媒介。

所有Buffer都继承抽象类Buffer,它定义了所有Buffer共同的属性和方法。除了Boolean之外,每种基本数据类型都有对应的Buffer类,如ByteBuffer、IntBuffer、CharBuffer等等。

Buffer四大属性

  • capacity:可存储的元素个数,非负,不可变
  • limit:第一个不可读/写元素的位置,非负,小于等于capacity
  • position:下一个可读/写元素的位置(可以类比游标,代表当前访问位置),非负,小于等于limit
  • mark:调用reset()方法后position的位置,Buffer初始化的时候是未定义mark的。

0 <= mark <= position <= limit <= capacity
在这里插入图片描述

创建Buffer

由于IO操作最终都是基于Byte的,所以ByteBuffer与其它Buffer大有不同。

ByteBuffer

ByteBuffer的创建主要有两种形式:allocate和wrap

allocate方法创建ByteBuffer并为它分配内存,元素初始化为0:

ByteBuffer allocate(int capacity);//backing array
ByteBuffer allocateDirect(int capacity);//direct byte buffer,Whether or not it has a backing array is unspecified.

wrap方法将现有的字节数组包装成一个ByteBuffer,所以这个数组就是它的支撑数组(backing array)。它们是绑定的,对Buffer对象的修改会影响到backing array,反之亦然。

ByteBuffer wrap(byte[] array); //position=0,limit=capacity=array.length
ByteBuffer wrap(byte[] array, int offset, int length); //capacity=array.length,position=offset, limit=offset+capacity

View Buffers

其它Buffers都不能直接IO,它们本质上都是按ByteBuffer存储的,只是为了方便我们使用基本数据类型操作Buffer,所以可以理解为是ByteBuffer的“视图”。

View Buffers意思就是视图Buffer,视图Buffer由原Buffer复制而来,它们的内容也是公用的,但拥有独立的属性。

ByteBuffer buffer = ByteBuffer.allocate(10);
ByteBuffer bufferView = buffer.duplicate(); 
CharBuffer charBuffer = buffer.asCharBuffer();

Buffer读写

Buffer读写分为两种:绝对读写和相对读写。

带index参数的为绝对读写,例如:

ByteBuffer put(int index, byte b) ;
byte get(int index);

不带index参数的为相对读写,例如:

ByteBuffer put(byte b) ;
byte get();

绝对读写基于传入index操作,不会改变position属性。
相对读写基于position属性操作,操作完之后position自增。

Clearing, flipping, and rewinding

方法动作用法
clear()limit=capacity,position=0准备新一次的channel read或相对put
flip()limit=position(),position=0准备新一次的channel write或相对get
rewind()limit不变,position=0重复读

compact

compact()方法把从position到limit之间的元素移动到buffer开始位置。假设这之间有n个元素,全部移动完后,position=n, limit=capacity.

Invoke this method after writing data from a buffer in case the write was incomplete.

假设这种情况,通过一个buffer从in channel复制数据到out channel:

buf.clear(); // Prepare buffer for use
while (in.read(buf) >= 0 || buf.position != 0) {
	buf.flip();
	out.write(buf);
	buf.compact(); // In case of partial write
}

Direct Byte Buffer

操作系统可以直接访问进程的地址空间。例如,操作系统可以直接访问JVM的地址空间(堆),基于字节数组来转移数据。然而JVM可能并不会连续地存储字节数组,并且GC也会移动这些内存(某些使用Compact的回收器)。

A byte buffer is either direct or non-direct.
A direct byte buffer is a byte buffer that interacts with channels and native code to perform I/O.

direct byte buffer是性能最高的,JVM会尽量直接执行操作系统的native IO,避免了JVM缓冲区复制到系统内核缓冲区,反之亦然。

当给channel传递一个non-direct byte buffer时,channel可能要先创建一个临时的direct byte buffer,把non-direct byte buffer的内容复制到这个临时direct byte buffer, 在这个临时direct byte buffer上执行IO操作,然后再把数据复制回non-direct byte buffer,最后这个临时的direct byte buffer可以被GC。

A direct byte buffer may be created by invoking the allocateDirect factory method of this class.

direct byte buffer的内存分配和回收代价都更高。它可能位于堆外内存,因此对JVM堆内存影响可能不明显,在分配内存时要特别注意。

因此建议直接内存只用于依赖于操作系统native IO的长期存活的大内存。只有当它们确实能很大提升性能的时候才决定使用。

A direct byte buffer may also be created by mapping a region of a file directly into memory.

JVM实现中,可能会使用JNI来分配direct byte buffer,如果这些类型的缓冲区之一的实例引用了一个不可访问的内存区域,那么访问该区域的尝试将不会更改缓冲区的内容,并且会导致在访问时或之后某个时间抛出未指定的异常。

Byte Ordering

Java中基本数据类型字节长度:

  • byte和boolean:1个字节
  • char和short:2个字节(所以char是可以存一个中文字的)
  • int和float:4个字节
  • long和double:8个字节

在计算机中,数据以二进制01序列表示,1个字节占8位,两个字节16位 …

计算机用一个字节(8位)作为最小寻址单元。
内存中每一个字节都有一个唯一标识,叫做地址
所有可能的地址集合叫做地址空间

对于单字节数据类型,所以可以存储在一个地址单元里。

而对于多字节数据类型,就要存储在多个连续的地址单元里,并且最小的地址单元作为数据的地址。

这种情况下,就涉及到字节序(byte order)的问题,不同的系统可能会采用不同的字节序。

  • 大端(big endian):低字节优先,即它的地址是低8位所在的地址
  • 小端(little endian):高字节优先,即它的地址是高8位所在的地址

例如,int num=0x01234567,它在内存中可能是这样的:
在这里插入图片描述
byte order问题在一般开发情况下可以不用理会,但如果通过网络IO相互通信时,就必须在通信协议中考虑两个系统byte order不一致的情况!

java.nio.ByteOrder 类提供了处理byte order相关问题的支持。

ByteOrder nativeOrder()//返回操作系统使用的字节序,ByteOrder.BIG_ENDIAN或ByteOrder.LITTLE_ENDIAN 

每种多字节类型对应的Buffer都提供了order()方法,返回这个Buffer使用的byte order。

ByteOrder order();

如果是allocate或wrap创建的buffer,byte order是操作系统默认。
如果是view buffer,byte order是原始的buffer的byte order,且view buffer的byte order不可改变。

ByteBuffer则不同,它们的默认byte order是大端的。

因为Java默认使用的byte order是大端的,ByteBuffer与之保持一致的话方便类加载和序列化等操作。

为了适应小端系统,ByteBuffer提供了order方法改变byte order:

ByteBuffer order(ByteOrder bo); 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值