This chapter mainly explains how to use direct memory (off heap memory), and explains it according to the following steps:
Related background -- > read / write operation -- > key attribute -- > read / write practice -- > extension -- > reference description
Hope to provide some quick reference for friends who want to use direct memory.
data type
The following are some common knowledge necessary for using DirectBuffer. Let's understand it for the time being!
Basic type length
There are many basic types in Java, such as:
- Byte, a byte is 8 bit s, that is 1B
- short, 16 bit, that is 2B
- int, 32-bit, i.e. 4B
- long, 64 bit, i.e. 8B
- char, 16 bit, i.e. 2B
- float, 32-bit, i.e. 4B
- double, 64 bit, i.e. 8B
Different types will be stored according to their own bits, and can be converted and promoted automatically.
byte, char and short can be automatically promoted to int. if the operand has long, it will be automatically promoted to long. The same is true for float and double.
Big end small end
Since a data type may be composed of many bytes, how are they placed. This is particular:
- Big end: low address bits store high significant bytes
- Small end: low address bits store low significant bytes
For example, a char is composed of two bytes. The storage of these two bytes may look like the following, such as character a:
1 2 3 | Low address bit and high address bit Big end; 00 96 Small end: 96 # 00 |
The difference between String and new String
The difference between "hello" and new String("hello"):
If it is "hello", the JVM will first go to the shared string pool to find whether there is the word "hello". If so, it will directly return its reference; If not, this object will be created and returned. Therefore, "a"+"b" is equivalent to the existence of three objects, namely "a", "b" and "ab".
The new String("hello") eliminates the search process and directly creates a hello object and returns a reference.
Read and write data
In direct memory, apply for direct memory through allocateDirect(int byte_length). This memory can be understood as an ordinary Byte based array, so the insertion and reading are similar to ordinary arrays. The allocateDirect method in ByteBuffer calls DirectByteBuffer.
public static ByteBuffer allocateDirect(int capacity) { return new DirectByteBuffer(capacity); }
It only provides insertion methods based on different data types, such as:
- put(byte) inserts a byte
- put(byte []) inserts a byte array
- putChar(char) insert character
- putInt(int) insert Int
- putLong(long) insert long
Wait For detailed usage, please refer to the following pictures:
Corresponding to reading data, it is similar to writing:
Note that all methods without the index parameter operate according to the current position.
Let's take a look at what position is and what other attributes it has!
Basic attribute values
It has several key indicators:
mark-->position-->limit-->capacity
Let's talk about what they mean first! In addition, there is retaining = limit position.
Current position
Position is a pointer to the current array, indicating the current data position. for instance:
ByteBuffer buffer = ByteBuffer.allocateDirect(1024); buffer.putChar('a'); System.out.println(buffer); buffer.putChar('c'); System.out.println(buffer); buffer.putInt(10); System.out.println(buffer);
Since a char is 2 bytes and an Int is 4 bytes, the position s are 2, 4 and 8 respectively
Note that the Position is the current Position of the inserted data. If the data is inserted, it will move back automatically.
In other words, if two bytes of data are stored, the position is on the third byte, and the subscript is 2.
java.nio.DirectByteBuffer[pos=2 lim=1024 cap=1024] java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024] java.nio.DirectByteBuffer[pos=8 lim=1024 cap=1024]
- Position can be obtained through position() or set through position(int).
//position(int) method source code public final Buffer position(int newPosition) { if ((newPosition > limit) || (newPosition < 0)) throw new IllegalArgumentException(); position = newPosition; if (mark > position) mark = -1; return this; }
Note: position is smaller than limit and larger than mark
Space capacity
Capacity is the direct memory capacity currently applied for, which will not change after application.
- Capacity can be obtained through the capacity() method.
Limit size - limit
We may want to change the size of this direct memory, so we can set it through a property called Limit.
- Limit can be obtained through limit() and set through limit(int).
Note that limit is larger than mark and position and smaller than capacity.
//Source code of limit(int) method public final Buffer limit(int newLimit) { if ((newLimit > capacity) || (newLimit < 0)) throw new IllegalArgumentException(); limit = newLimit; if (position > limit) position = limit; if (mark > limit) mark = -1; return this; }
Mark position - mark
Mark is just a mark that records the value of the current position. A common scenario is to record the location of the inserted data at one time to facilitate the next backtracking.
- You can use the mark() method to mark,
- Use the reset() method to clear,
- Use the rewind() method for initialization
//The mark method marks the current position, which is - 1 by default public final Buffer mark() { mark = position; return this; } //The reset method resets the position of mark. The position cannot be less than the position of mark, otherwise an error will occur public final Buffer reset() { int m = mark; if (m < 0) throw new InvalidMarkException(); position = m; return this; } //Reset mark to - 1 Position is 0 public final Buffer rewind() { position = 0; mark = -1; return this; }
Use case
ByteBuffer buffer = ByteBuffer.allocateDirect(1024); buffer.putChar('a'); buffer.putChar('c'); System.out.println("Insert data " + buffer); buffer.mark();// Record the location of the mark buffer.position(30);// The set position must be larger than mark, otherwise mark cannot be reset System.out.println("reset front " + buffer); buffer.reset();// Reset, position after reset = mark System.out.println("reset after " + buffer); buffer.rewind();//Clear the mark, position becomes 0 and mark becomes - 1 System.out.println("After clearing the mark " + buffer);
You can see the following operation results:
Insert data java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024] reset front java.nio.DirectByteBuffer[pos=30 lim=1024 cap=1024] reset after java.nio.DirectByteBuffer[pos=4 lim=1024 cap=1024] After clearing the mark java.nio.DirectByteBuffer[pos=0 lim=1024 cap=1024]
Remaining space
Remaining indicates the current remaining space:
public final int remaining() { return limit - position; }
Reading and writing practice
The write operation is mainly to write to the direct memory according to its own data type. Note that each time the data is written, the position will automatically add the length of the written data to point to the starting position of the next write:
Let's see how to write a byte [] or string:
public static void test1() { ByteBuffer buffer = ByteBuffer.allocateDirect(10); byte[] data = {1,2}; buffer.put(data); System.out.println("write byte[]after " + buffer); buffer.clear();//Empty buffer.put("hello".getBytes());//5 byte s System.out.println("hello".getBytes().length); System.out.println("write string after " + buffer); }
result:
write byte[]after java.nio.DirectByteBuffer[pos=2 lim=10 cap=10] 5 write string after java.nio.DirectByteBuffer[pos=5 lim=10 cap=10]
When reading, you can read through an external byte [] array. Because there is no way to directly operate the direct memory: therefore, if you want to use the direct memory in the JVM application, you need to apply for a section of space in the heap to store data.
If there is a better way, please leave a message.
ByteBuffer buffer = ByteBuffer.allocateDirect(10); buffer.put(new byte[]{1,2,3,4}); System.out.println("Just finished writing the data " +buffer); buffer.flip(); System.out.println("flip after " +buffer); byte[] target = new byte[buffer.limit()]; buffer.get(target);//Automatically read target Length data for(byte b : target){ System.out.println(b); } System.out.println("Finished reading array " +buffer);
Output as
Just finished writing the data java.nio.DirectByteBuffer[pos=4 lim=10 cap=10] flip after java.nio.DirectByteBuffer[pos=0 lim=4 cap=10] 1 2 3 4 Finished reading array java.nio.DirectByteBuffer[pos=4 lim=4 cap=10]
common method
In the above reading and writing examples, there are several common methods:
clear()
This method is used to clear the mark, position and the position of limit:
public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this; }
flip()
This method is mainly used to change the current Position to limit, which is mainly used for reading operations.
public final Buffer flip() { limit = position; position = 0; mark = -1; return this; }
compact()
This method is commonly used when reading part of the data.
It will move the current Position to 0, and then position+1 to 1.
public ByteBuffer compact() { int pos = position(); int lim = limit(); assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); unsafe.copyMemory(ix(pos), ix(0), rem << 0); position(rem); limit(capacity()); discardMark(); return this; }
For example, the content of a space is:
123456789
When the position is at 2, call the compact method and it will become:
345678989
isDirect()
This method is used to determine whether it is direct memory. If it is true, if not false.
rewind()
This method is used to reset the mark mark:
public final Buffer rewind() { position = 0; mark = -1; return this; }