Java NIO Buffer buffer

Introduction to buffers

The operating system has the concept of user space and system space. The JAVA process corresponding to the JVM is located in the user space, and the process in this space cannot directly access the hardware device. When the JAVA process wants to perform I/O operations, it can only use system calls. Control is given to the kernel, and the kernel prepares the data required by the process and copies the data to the user space buffer (as shown in the following figure).

Buffers in Java NIO are used to interact with NIO channels. As you know, data is read from the channel into the buffer, and written from the buffer to the channel.

A buffer is essentially a block of memory to which data can be written and then read from it. This piece of memory is wrapped as a NIO Buffer object and provides a set of methods for easy access to this piece of memory.

In order to understand how Buffer works, you need to be familiar with its three properties:

  • capacity
  • position
  • limit

The meaning of position and limit depends on whether the Buffer is in read mode or write mode. No matter what mode the Buffer is in, the meaning of capacity is always the same.

Here is a description of capacity, position and limit in read and write mode, the detailed explanation is after the illustration.

capacity

As a memory block, Buffer has a fixed size value, also called "capacity". You can only write capacity bytes, long, char and other types into it. Once the Buffer is full, it needs to be emptied (by reading data or clearing data) to continue writing data to it.

position

When you write data to the Buffer, position represents the current position. The initial position value is 0. When a byte, long and other data are written to the Buffer, the position will move forward to the next Buffer unit where data can be inserted. position can be up to capacity – 1.

When reading data, it is also reading from a specific location. When switching the Buffer from write mode to read mode, the position is reset to 0. When reading data from the Buffer's position, the position moves forward to the next readable position.

limit

In write mode, the limit of the Buffer indicates how much data you can write to the Buffer at most. In write mode, limit is equal to the capacity of Buffer.

When switching Buffer to read mode, limit indicates how much data you can read at most. Therefore, when switching the buffer to read mode, limit will be set to the position value in write mode. In other words, you can read all the previously written data (the limit is set to the number of written data, which is the position in write mode)

Buffer method

flip(): Switch the Buffer read mode to write mode, set the position to 0, and set the limit to the value of the previous position.

clear(): Clear the entire buffer, position will be set back to 0, limit will be set to the value of capacity. The data in the Buffer is not cleared. If there is some unread data in the Buffer, call the clear() method, the data will be "Forgotten" means that there are no longer any markers that will tell you which data has been read and which has not.

compact(): Only data that has been read will be cleared. Any unread data is moved to the beginning of the buffer, newly written data is placed after the unread data in the buffer, and the position is set to just after the last unread element. The limit property is still set to capacity like the clear() method. The buffer is now ready to write data, but will not overwrite unread data.

allocate(1024): Initialize the Buffer, and the set value determines the size of the capacity value

rewind(): Set the position back to 0, so you can reread all the data in the Buffer. The limit remains the same, still indicating how many elements (byte, char, etc.) can be read from the Buffer

mark() and reset(): By calling the Buffer.mark() method, you can mark a specific position in the Buffer. You can then restore to this position by calling the Buffer.reset() method

 

equals(): Two Buffers are equal when the following three conditions are met

  • have the same type (byte, char, int, etc.)
  • The number of bytes, chars, etc. remaining in the Buffer is equal
  • Buffer中所有剩余的byte、char等都相同

只比较的是剩余的数据

compareTo():满足下列条件,则认为一个Buffer“小于”另一个Buffer

  • 第一个不相等的元素小于另一个Buffer中对应的元素
  • 所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)

注意点:

  1. ByteBuffer byteBuffer = ByteBuffer.allocate(1);在从通道往buffer中读入之后,使用byteBuffer.get()获取的时候,不可重复调用,因为get()方法会移动position,使得多次调用get()方法获取的内容是不同的

  2. ByteBuffer中,put(int index, byte b)方法不会移动position,但是put(byte b)会移动position

  3. ByteBuffer.allocate(int capacity)ByteBuffer.allocateDirect(int capacity)的区别:使用allocate来创建缓冲区,并不是一下子就分配给缓冲区capacity大小的空间,而是根据缓冲区中存储数据的情况来动态分配缓冲区的大小(实际上,在底层Java采用了数据结构中的堆来管理缓冲区的大小),因此,这个capacity可以是一个很大的值,如1024*1024(1M)。使用allocateDirect(由操作系统分配,脱离了JVM)方法可以一次性分配capacity大小的连续字节空间。通过allocateDirect方法来创建具有连续空间的ByteBuffer对象虽然可以在一定程度上提高效率,但这种方式并不是平台独立的。也就是说,在一些操作系统平台上使用allocateDirect方法来创建ByteBuffer对象会使效率大幅度提高,而在另一些操作系统平台上,性能会表现得非常差。而且allocateDirect方法需要较长的时间来分配内存空间,在释放空间时也较慢。因此,在使用allocateDirect方法时应谨慎

参考链接:

http://www.jianshu.com/p/052035037297

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327026775&siteId=291194637