Buffer

作者: 水欣 | 来源:发表于2017-11-04 18:15 被阅读0次
    1. Java NIO Buffer
      当我们需要与NIO Channel进行交互时,我们就需要使用到NIO Buffer,即数据从Buffer读取到Channel中,并且从Channel中写入到Buffer中。
      实际上,一个Buffer其实就是一块内存区域,我们可以在这个内存区域中进行数据的读写。NIO Buffer其实是这样的内存块的一个封装,并提供了一些操作方法让我们能够方便地进行数据的读写。
    2. Buffer类型有:
    • ByteBuffer
    • CharBuffer
    • DoubleBuffer
    • FloatBuffer
    • IntBuffer
    • LongBuffer
    • ShortBuffer
    1. NIO Buffer的基本使用
      使用NIO Buffer的步骤如下:
    • 将数据写入Buffer中

    • 调用Buffer.flip()方法,将NIO Buffer转换为读模式

    • 从Buffer中读取数据

    • 调用Buffer.clear()或Buffer.compact()方法,将Buffer转换为写模式。
      当我们将数据写入到Buffer中时,Buffer会记录我们已经写了多少的数据,当我们需要从Buffer中读取数据时,必须调用Buffer.flip()将Buffer切换为度模式。
      一旦读取了所有的Buffer数据,那么我们必须清理Buffer,让其从新可写,清理Buffer可以调用Buffer.clear()或Buffer.compact()

      public class Test {
          public static void main(String[] args) {
          IntBuffer intBuffer = IntBuffer.allocate(2);
          intBuffer.put(124342);
          intBuffer.put(3);
         intBuffer.flip();
        System.out.println(intBuffer.get());
        System.out.println(intBuffer.get());
        }
      }
      

    分配了两个单位大小的IntBuffer,因此他可以写入两个int值
    我们使用put方法将int值写入,然后使用flip方法将buffer转换为读模式,然后连续使用get方法从buffer中获取这两个int值。
    没当调用一次get方法读取数据时,buffer的读指针就会向前移动一个单位长度(在这里是一个int长度)。

    1. Buffer属性
      一个Buffer由三个属性:
    • capacity
    • position
    • limit
      其中position和limit的含义和Buffer处于读模式或写模式有关,而capacity的含义与Buffer所处的模式无关
    1. capacity
      一个内存块会有一个固定的大小,即容量(capacity),我们最多写入capacity个单位的数据到Buffer中,例如一个DoubleBuffer,其Capacity是100,那么我们做多可以写入100个double数据。

    2. position
      当从一个Buffer中写入数据时,我们是从Buffer的一个确定的位置(position)开始写入。在最初的状态时,position的值是0。每当我们写入了一个单位的数据后,position就会递增一。
      当我们从Buffer中读取数据时,我们也是从某个特定的位置开始读取。当我们调用了flip()方法将Buffer从写模式转换到读模式时,position的值会自动被设置为0,每当我们读取一个单位的数据,position的值递增1。
      position表示了读写操作的位置指针。

    3. limit
      limit-position表示此时还可以写入/读取多少单位的数据

    4. 分配Buffer
      为了获取一个Buffer对象,我们首先需要分配内存空间。每个类型的Buffer都有一个allocate()方法,我们可以通过这个方法分配Buffer

      ByteBuffer buf = ByteBuffer.allocate(48);
      

    这里我们分配了48*sizeof(Byte)字节的内存空间

    1. 关于Direct Buffer和Non-Direct Buffer的区别
    • Direct Buffer:

      • 所分配的内存不在JVM堆上,不受GC的管理(但是Direct Buffer的java对象是由GC管理的,因此当发生GC,对象被回收时,Direct Buffer也会被释放)
      • 因为Direct Buffer不在JVM堆上分配,因此Direct Buffer对应用程序的内存占用的影响就不那么明显(实际上还是镇用了那么多内存,但是JVM不好统计到非JVM管理的内存)
      • 申请和释放Direct Buffer的开销比较大,因此正确的使用Direct Buffer的方式是在初始化时申请一个Buffer,然后不断复用此Buffer,在程序结束后才释放此Buffer
      • 使用Direct Buffer时,当进行一些底层的系统IO操作时,效率会比较高,因为此时JVM不需要拷贝buffer中的内存到中间临时缓冲区。
    • Non-Direct buffer

      • 直接在JVM堆上进行内存的分配,本质上市byte[]数组的封装
      • 因为Non-Direct Buffer在JVM堆中,因此当进行操作系统底层IO操作时,会将次Buffer的内存复制到中间临时缓冲区,因此Non-Direct buffer的效率就较低
    1. 写数据到Buffer

      int bytesRead = inChannel.read(buf); //read into buffer.
      buf.put(127);
      
    2. 从Buffer中读取数据

          //read from buffer into channel.
      int bytesWritten = inChannel.write(buf);
      byte aByte = buf.get();
      
    3. 重置position
      Buffer.rewind()方法可以重置position的值为0,因此我们可以重新读取/写入Buffer了,如果是读模式,则重置的是读模式的position,如果是写模式,则重置的是写模式的position

    4. mark()和reset()
      我么可以通过调用Buffer.mark()将当前的position的值保存起来,随后可以通过调用Buffer.reset()方法将position的值恢复回来

      public class Test {
      
               public static void main(String[] args) {
               IntBuffer intBuffer = IntBuffer.allocate(2);
               intBuffer.put(1);
               intBuffer.put(2);
      
              intBuffer.flip();
              System.out.println(intBuffer.get());
              intBuffer.mark();
              System.out.println(intBuffer.get());
      
             System.out.println(intBuffer.position());
             intBuffer.reset();
            System.out.println(intBuffer.position());
            System.out.println(intBuffer.get());
          }
      }
      

    这里我们写入两个int值,然后首先读取了一个值,此时读position的值为1
    接着我们调用mark()方法将当前的position保存起来(在读模式,因此保存的是position),然后再次读取,此时position就是2了,接着使用reset()恢复原来的读position,因此读position就为1,可以再次读取数据。

    1. flip,rewind和clear的区别
    • flip
      源码

      public final Buffer flip() {
      limit = position;
      position = 0;
      mark = -1;
      return this;
      }
      

    Buffer的读/写模式公用一个position和limit变量
    当从写模式变为读模式时,原来的写position就变成了读模式的limit

    • rewind
      源码

       public final Buffer rewind() {
       position = 0;
        mark = -1;
        return this;
      }
      

    rewind,即倒带,这个方法仅仅是将position置为0

    • clear
      源码

      public final Buffer clear() {
      position = 0;
      limit = capacity;
      mark = -1;
      return this;
      }  
      

    根据源码我们知道,clear将position设置为0,将limit设置为capacity
    clear方法使用场景
    1 在一个已经写满数据的Buffer中,调用clear,可以从头读取buffer的数据。
    2 为了将一个Buffer填充慢数据,可以调用clear,然后一直写入,直到达到limit

    相关文章

      网友评论

          本文标题:Buffer

          本文链接:https://www.haomeiwen.com/subject/geafmxtx.html