NIO之ByteBuffer的使用

NIO中有三大组件:Channel、Selector、Buffer,今天就来讲讲Buffer的使用。

Buffer的继承结构:

NIO之ByteBuffer的使用_limit

Buffer为java中的每个数据类型都提供了对应的实现类,但是实际使用中用得最多的还是ByteBuffer。

ByteBuffer的创建

在堆上创建:

log.info("freeMemory:{}MB", Runtime.getRuntime().freeMemory() / _1_MB);
ByteBuffer allocate = ByteBuffer.allocate(20 * _1_MB); // 堆上内存分配
log.info("freeMemory:{}MB", Runtime.getRuntime().freeMemory() / _1_MB);

运行结果如下:

2020-11-04 15:49:01,421  INFO [main] (ByteBufferNewDemo.java:20) - freeMemory:230MB
2020-11-04 15:49:01,436  INFO [main] (ByteBufferNewDemo.java:23) - freeMemory:209MB

在直接内存上创建:

OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
log.info("freeMemory:{}MB", osmxb.getFreePhysicalMemorySize() / _1_MB);
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(20 * _1_MB);
log.info("freeMemory:{}MB", osmxb.getFreePhysicalMemorySize() / _1_MB);

运行结果如下:

2020-11-04 15:53:38,808  INFO [main] (ByteBufferNewDemo.java:35) - freeMemory:4524MB
2020-11-04 15:53:38,816  INFO [main] (ByteBufferNewDemo.java:37) - freeMemory:4503MB

ByteBuffer的使用

ByteBuffer有三个重要的属性:

  • capacity:ByteBuffer底层其实是一个字节数组,capacity代表数组的长度。
  • position:表示下一个要操作(读或写)的元素的位置。
  • limit:在写模式下,limit=capacity,在读模式下,limit代表数组中实际元素的个数。

get和put值

ByteBuffer byteBuffer = ByteBuffer.allocate(8);
byteBuffer.put((byte) 'a');
byteBuffer.put((byte) 'b');
byteBuffer.put((byte) 'c');
byteBuffer.put((byte) 'd');
log.info("put a b c d, {}", byteBuffer);
byteBuffer.flip(); // 切换为读模式
log.info("after flip, {}", byteBuffer);
log.info("get byte: {}", (char) byteBuffer.get());
log.info("after get, {}", byteBuffer);

运行结果如下:

2020-11-04 16:04:08,130  INFO [main] (ByteBufferNewDemo.java:47) - put a b c d, java.nio.HeapByteBuffer[pos=4 lim=8 cap=8]
2020-11-04 16:04:08,133  INFO [main] (ByteBufferNewDemo.java:49) - after flip, java.nio.HeapByteBuffer[pos=0 lim=4 cap=8]
2020-11-04 16:04:08,133  INFO [main] (ByteBufferNewDemo.java:50) - get byte: a
2020-11-04 16:04:08,133  INFO [main] (ByteBufferNewDemo.java:51) - after get, java.nio.HeapByteBuffer[pos=1 lim=4 cap=8]

NIO之ByteBuffer的使用_bytebufffer_02

一次性get

ByteBuffer byteBuffer = ByteBuffer.allocate(8);
byteBuffer.put((byte) 'a');
byteBuffer.put((byte) 'b');
byteBuffer.put((byte) 'c');
byteBuffer.put((byte) 'd');
byteBuffer.flip(); // 切换为读模式
byte[] bytes = new byte[byteBuffer.remaining()];
log.info("before get, {}", byteBuffer);
byteBuffer.get(bytes, 0, bytes.length);
log.info("bytes:{}", new String(bytes));
log.info("after get, {}", byteBuffer);

运行结果如下:

2020-11-04 16:32:36,032  INFO [main] (ByteBufferNewDemo.java:64) - before get, java.nio.HeapByteBuffer[pos=0 lim=4 cap=8]
2020-11-04 16:32:36,034  INFO [main] (ByteBufferNewDemo.java:66) - bytes:abcd
2020-11-04 16:32:36,034  INFO [main] (ByteBufferNewDemo.java:67) - after get, java.nio.HeapByteBuffer[pos=4 lim=4 cap=8]

指定位置读和写

使用put和get方法时,指定index进行操作,不会改变position的位置。

ByteBuffer byteBuffer = ByteBuffer.allocate(8);
byteBuffer.put(0, (byte) 'a');
byteBuffer.put(1, (byte) 'b');
byteBuffer.put(2, (byte) 'c');
byteBuffer.put(3, (byte) 'd');
log.info("put a b c d, {}", byteBuffer);
log.info("get byte: {}", (char) byteBuffer.get(0));
log.info("after get, {}", byteBuffer);

运行结果如下:

2020-11-04 16:35:23,152  INFO [main] (ByteBufferNewDemo.java:77) - put a b c d, java.nio.HeapByteBuffer[pos=0 lim=8 cap=8]
2020-11-04 16:35:23,154  INFO [main] (ByteBufferNewDemo.java:78) - get byte: a
2020-11-04 16:35:23,154  INFO [main] (ByteBufferNewDemo.java:79) - after get, java.nio.HeapByteBuffer[pos=0 lim=8 cap=8]

clear

clear()方法会加将position设置为0,limit设置成capacity,也就是说ByteBuffer被清空了,其实Buffer中的数据并未被清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据。如果Buffer中有一些未读的数据,调用clear()方法,数据将“被遗忘”,意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。

ByteBuffer byteBuffer = ByteBuffer.allocate(32);
byteBuffer.put((byte) 'x');
log.info("before clear: {}" + byteBuffer);
byteBuffer.clear();
log.info("after clear: {}" + byteBuffer);

运行结果如下:

2020-11-04 16:44:36,956  INFO [main] (ByteBufferNewDemo.java:86) - before clear: {}java.nio.HeapByteBuffer[pos=1 lim=32 cap=32]
2020-11-04 16:44:36,958  INFO [main] (ByteBufferNewDemo.java:88) - after clear: {}java.nio.HeapByteBuffer[pos=0 lim=32 cap=32]

compact

compact()方法将所有未读的数据拷贝到Buffer起始处(压缩),然后将position设到最后一个未读元素正后面,limit属性依然像clear()方法一样,设置成capacity。

ByteBuffer byteBuffer = ByteBuffer.allocate(8);
byteBuffer.put((byte) 'a');
byteBuffer.put((byte) 'b');
byteBuffer.put((byte) 'c');
byteBuffer.put((byte) 'd');
byteBuffer.flip(); // 切换为读模式
byteBuffer.get();
log.info("before compact: {}", byteBuffer);
byteBuffer.compact();
log.info("after compact: {}", byteBuffer);
byteBuffer.flip(); // 切换为读模式
log.info("{}",byteBuffer.remaining());

运行结果如下:

2020-11-04 16:51:19,010  INFO [main] (ByteBufferNewDemo.java:100) - before compact: java.nio.HeapByteBuffer[pos=1 lim=4 cap=8]
2020-11-04 16:51:19,013  INFO [main] (ByteBufferNewDemo.java:102) - after compact: java.nio.HeapByteBuffer[pos=3 lim=8 cap=8]
2020-11-04 16:51:19,013  INFO [main] (ByteBufferNewDemo.java:104) - 3

rewind

rewind()方法会将position设置为0,limit的值不变,类似磁带的倒带功能。

ByteBuffer byteBuffer = ByteBuffer.allocate(16);
byteBuffer.position(10);
byteBuffer.limit(15);
log.info("before rewind: {}", byteBuffer);
byteBuffer.rewind();
log.info("after rewind: {}", byteBuffer);

运行结果如下:

2020-11-04 16:56:53,935  INFO [main] (ByteBufferNewDemo.java:112) - before rewind: java.nio.HeapByteBuffer[pos=10 lim=15 cap=16]
2020-11-04 16:56:53,939  INFO [main] (ByteBufferNewDemo.java:114) - after rewind: java.nio.HeapByteBuffer[pos=0 lim=15 cap=16]

mark和reset

ByteBuffer中有一个属性用来存放临时的位置下标,调用mark()会将mark设为当前的position的值,以后调用reset()会将position属性设置为mark的值。

ByteBuffer中的几个属性总是满足以下关系:0 <= mark <= position <= limit <= capacity

ByteBuffer byteBuffer = ByteBuffer.allocate(16);
byteBuffer.position(10);
byteBuffer.mark();
byteBuffer.position(15);
log.info("before reset: {}", byteBuffer);
byteBuffer.reset();
log.info("after reset: {}", byteBuffer);

运行结果如下:

2020-11-04 17:00:13,572  INFO [main] (ByteBufferNewDemo.java:123) - before reset: java.nio.HeapByteBuffer[pos=15 lim=16 cap=16]
2020-11-04 17:00:13,577  INFO [main] (ByteBufferNewDemo.java:125) - after reset: java.nio.HeapByteBuffer[pos=10 lim=16 cap=16]