> 文章列表 > ByteBuffer总结

ByteBuffer总结

ByteBuffer总结

文章目录

  • 1、Buffer介绍
  • 2、buffer运用
  • 3、直接内存和堆内存写和读性能对比

1、Buffer介绍

Buffer 类是 java.nio 的构造基础。一个 Buffer 对象是固定数量的、数据的容器,其作用是一个存储器或者分段运输区。在这里,数据可被存储并在之后用于检索。缓冲区可以被写满或释放。对于每个非布尔类型的、原始数据类型都有一个缓冲区类,即 Buffer 的子类有:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer 和 ShortBuffer,是没有 BooleanBuffer 之说的。尽管缓冲区作用于它们存储的原始数据类型,但缓冲区十分倾向于处理字节。
Buffer及其子类都不是线程安全的,若多线程操作该缓冲区,则应通过同步来控制对该缓冲区的访问。

2、buffer运用

每个Buffer都有以下四个属性:
(1) capacity:容量,缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,且永远不能被改变
(2) limit:上界,缓冲区的第一个不能被读或写的元素。缓冲区创建时,limit 的值等于 capacity 的值。假设 capacity = 1024,我们在程序中设置了 limit = 512,说明Buffer 的容量为 1024,但是从 512 之后既不能读也不能写,因此可以理解成,Buffer 的实际可用大小为 512
(3) position:位置,下一个要被读或写的元素的索引。位置会自动由相应的 get() 和 put() 函数更新。 这里需要注意的是positon的位置是从0开始,比如,已经写入buffer 3个元素那那么position就是指向第4个位置,即position设置为3(数组从0开始计)
(4) mark:标记,一个备忘位置。保存某个时刻的position指针的值,通过调用mark()实现,当mark被置为负值时,表示废弃标记。标记在设定前是未定义的(undefined)。使用场景是,假设缓冲区中有 10 个元素,position 目前的位置为 2(也就是如果get的话是第三个元素),现在只想发送 6 - 10 之间的缓冲数据,此时我们可以 buffer.mark(buffer.position());即把当前的 position 记入 mark 中,然后 buffer.postion(6);此时发送给 channel 的数据就是 6 - 10 的数据。发送完后,我们可以调用 buffer.reset() 使得 position = mark,因此这里的 mark 只是用于临时记录一下位置用的
(5) position和limit之间的距离指定了可读/写的字节数
(6) mark,position,limit,capacity大小关系:
-1 <= mark <= position <= limit <= capacity
0<= position <= limit <= capacity

package com.msb.netty.buffer;import java.nio.ByteBuffer;/* 类说明:Buffer方法演示*/
public class BufferMethod {public static void main(String[] args) {System.out.println("------Test get-------------");ByteBuffer buffer = ByteBuffer.allocate(32);buffer.put((byte) 'a')//0.put((byte) 'b')//1.put((byte) 'c')//2.put((byte) 'd')//3.put((byte) 'e')//4.put((byte) 'f');//5System.out.println("before flip()" + buffer);/* 转换为读取模式*/buffer.flip();System.out.println("before get():" + buffer);System.out.println((char) buffer.get());System.out.println("after get():" + buffer);/* get(index)不影响position的值*/System.out.println((char) buffer.get(2));System.out.println("after get(index):" + buffer);byte[] dst = new byte[10];/* position移动两位*/buffer.get(dst, 0, 2);/*这里的buffer是 abcdef[pos=3 lim=6 cap=32]*/System.out.println("after get(dst, 0, 2):" + buffer);System.out.println("dst:" + new String(dst));System.out.println("--------Test put-------");ByteBuffer bb = ByteBuffer.allocate(32);System.out.println("before put(byte):" + bb);System.out.println("after put(byte):" + bb.put((byte) 'z'));// put(2,(byte) 'c')不改变position的位置bb.put(2, (byte) 'c');System.out.println("after put(2,(byte) 'c'):" + bb);System.out.println(new String(bb.array()));// 这里的buffer是 abcdef[pos=3 lim=6 cap=32]bb.put(buffer);System.out.println("after put(buffer):" + bb);System.out.println(new String(bb.array()));System.out.println("--------Test reset----------");buffer = ByteBuffer.allocate(20);System.out.println("buffer = " + buffer);buffer.clear();buffer.position(5);//移动position到5buffer.mark();//记录当前position的位置buffer.position(10);//移动position到10System.out.println("before reset:" + buffer);buffer.reset();//复位position到记录的地址System.out.println("after reset:" + buffer);System.out.println("--------Test rewind--------");buffer.clear();buffer.position(10);//移动position到10buffer.limit(15);//限定最大可写入的位置为15System.out.println("before rewind:" + buffer);buffer.rewind();//将position设回0System.out.println("before rewind:" + buffer);System.out.println("--------Test compact--------");buffer.clear();//放入4个字节,position移动到下个可写入的位置,也就是4buffer.put("abcd".getBytes());System.out.println("before compact:" + buffer);System.out.println(new String(buffer.array()));buffer.flip();//将position设回0,并将limit设置成之前position的值System.out.println("after flip:" + buffer);//从Buffer中读取数据的例子,每读一次,position移动一次System.out.println((char) buffer.get());System.out.println((char) buffer.get());System.out.println((char) buffer.get());System.out.println("after three gets:" + buffer);System.out.println(new String(buffer.array()));//compact()方法将所有未读的数据拷贝到Buffer起始处。// 然后将position设到最后一个未读元素正后面。buffer.compact();System.out.println("after compact:" + buffer);System.out.println(new String(buffer.array()));}
}

3、直接内存和堆内存写和读性能对比

直接内存 和 堆内存的 分配空间比较:
在数据量提升时,直接内存相比堆内存的申请,有很严重的性能问题
直接内存 和 堆内存的 读写性能比较:
结论:直接内存在直接的IO 操作上,在频繁的读写时 会有显著的性能提升
案例:

package com.msb.netty.buffer;import java.nio.ByteBuffer;/* 类说明:直接内存与堆内存的性能比较*/
public class ByteBufferCompare {public static void main(String[] args) {allocateCompare();   //分配比较operateCompare();    //读写比较}/* 直接内存 和 堆内存的 分配空间比较 结论: 在数据量提升时,直接内存相比堆内存的申请,有很严重的性能问题/public static void allocateCompare(){int time = 10000000;    //操作次数  1000Wlong st = System.currentTimeMillis();for (int i = 0; i < time; i++) {ByteBuffer buffer = ByteBuffer.allocate(2);      //非直接内存分配申请}long et = System.currentTimeMillis();System.out.println("在进行"+time+"次分配操作时,堆内存 分配耗时:" + (et-st) +"ms" );long st_heap = System.currentTimeMillis();for (int i = 0; i < time; i++) {ByteBuffer buffer = ByteBuffer.allocateDirect(2); //直接内存分配申请}long et_direct = System.currentTimeMillis();System.out.println("在进行"+time+"次分配操作时,直接内存 分配耗时:" + (et_direct-st_heap) +"ms" );}/* 直接内存 和 堆内存的 读写性能比较 结论:直接内存在直接的IO 操作上,在频繁的读写时 会有显著的性能提升/public static void operateCompare(){int time = 100000000;   //1亿次操作ByteBuffer buffer = ByteBuffer.allocate(2*time);long st = System.currentTimeMillis();for (int i = 0; i < time; i++) {buffer.putChar('a');}buffer.flip();for (int i = 0; i < time; i++) {buffer.getChar();}long et = System.currentTimeMillis();System.out.println("在进行"+time+"次读写操作时,堆内存读写耗时:" + (et-st) +"ms");ByteBuffer buffer_d = ByteBuffer.allocateDirect(2*time);long st_direct = System.currentTimeMillis();for (int i = 0; i < time; i++) {buffer_d.putChar('a');}buffer_d.flip();for (int i = 0; i < time; i++) {buffer_d.getChar();}long et_direct = System.currentTimeMillis();System.out.println("在进行"+time+"次读写操作时,直接内存读写耗时:" + (et_direct - st_direct) +"ms");}}