NIO-Buffer详解

每篇一句

温柔才是最锋利的武器,能够化解一切矛盾

一、Buffer简介

在使用NIO做交互时,需要使用到Buffer做数据的装载(如果说Channel是列车轨道,那么Buffer就是一节节车厢,车厢里面装的乘客就是需要传输的数据)
Buffer本质上就是一块内存区域并且提供了一些操作方法方便我们进行数据的读写

二、Buffer的基本使用

  1. 将数据写到Buffer中
  2. 调用Buffer.flip()方法将Buffer转为读模式
  3. 从Buffer中读取数据
例子:
public class Test {
	public static void main(String[] args) {
		 IntBuffer intBuffer = IntBuffer.allocate(10);
		 System.out.println("Write mode:");
		 intBuffer.put(10);
		 intBuffer.put(101);

		 intBuffer.flip();
		
                 System.out.println("Read mode: ");
		 System.out.println(intBuffer.get());
                 System.out.println(intBuffer.get());
	}
}

三、Buffer属性

Buffer有三个属性:capacity、position和limit

Capacity

  • 每个内存块都有一个固定大小也就是capacity,我们最多可以写入capacity个单位的数据到Buffer中
  • Capacity是固定的,不会随着读写模式的切换而变化

Position

  • 往Buffer写入数据时,我们是从Buffer的position开始写的;position初始值是0,每当写入一个单位的数据后,position会递增1
  • 当调用flip()从写模式切换为读模式时,position会被自动置零;每当读取一个单位的数据后,position会递增1
  • 当再次调用flip()从读模式切换为写模式时,会将limit赋值给position,limit是上次写的位置,相当于从上次写到的位置继续写

limit

  • limit - position 表示当前还可以写入/读取多少单位的数据
  • 这个值用来标记可以读/写的极端值以及用来记录上次写数据的位置方便后面续写
  • limit在写模式下等于capacity,在读模式下等于切换成读模式前的写模式下的position值,也就是写模式所写到的位置
例子:
public class Test {
	public static void main(String[] args) {
		 IntBuffer intBuffer = IntBuffer.allocate(10);
		 intBuffer.put(10);
		 intBuffer.put(101);
		System.out.println("Write mode:");
		System.out.println("Capacity: "+ intBuffer.capacity());
		System.out.println("Position: "+ intBuffer.position());
		System.out.println("Limit: "+ intBuffer.limit());
		intBuffer.put(102);
		System.out.println("after put data:");
		System.out.println("Capacity: "+ intBuffer.capacity());
		System.out.println("Position: "+ intBuffer.position());
		System.out.println("Limit: "+ intBuffer.limit());

		intBuffer.flip();
		System.out.println("Read mode: ");
		System.out.println("Capacity: "+ intBuffer.capacity());
		System.out.println("Position: "+ intBuffer.position());
		System.out.println("Limit: "+ intBuffer.limit());
		intBuffer.get();
		System.out.println("after read data:");
		System.out.println("Capacity: "+ intBuffer.capacity());
		System.out.println("Position: "+ intBuffer.position());
		System.out.println("Limit: "+ intBuffer.limit());
	}
}

四、分配/读写Buffer

1、分配Buffer

在获取Buffer对象时需要先分配内存空间,每个类型的Buffer都提供了一个allocate()方法,我们可以通过这个方法分配Buffer

分配10*sizeof(Byte)字节的内存空间
ByteBuffer byteBuffer = ByteBuffer.allocate(10);

分配1024*sizeof(Char)字节的内存空间
CharBuffer charBuffer = CharBuffer.allocate(1024);

2、写Buffer

int bytesRead = inChannel.read(buf);
buf.put(127);

3、读Buffer

int bytesWrite = inChannel.write(buf);
byte aByte = buf.get();

五、Direct Buffer和Non-Direct Buffer的区别

Direct Buffer

  • 所分配的内存不在JVM堆上,但是GC时可以通过Direct Buffer对象来释放这块空间的内存资源
  • 申请和释放Direct Buffer的开销较大,因此正确使用Direct Buffer的方式都是在初始化的時候申请Buffer,然后不断复用此Buffer直到程序结束后才进行释放
  • 使用Direct Buffer时,在进行底层系统IO操作时JVM不需要拷贝Buffer内存中的数据到临时缓冲区中,因此效率会更高

Non-Direct Buffer

  • 直接在JVM堆上进行内存分配,本质上是byte[]数组的封装
  • 由于是在JVM堆上分配,在操作系统底层IO操作时会需要将Buffer内存中的数据拷贝到临时缓冲区中,导致效率偏低

参考文献

  1. https://segmentfault.com/a/1190000006824155
  2. https://www.jianshu.com/p/9a1327559987
posted @ 2020-10-10 10:47  墨小雨的猫  阅读(225)  评论(0)    收藏  举报