preface
After understanding the principle of ByteBuffer, it is easier to understand Netty's ByteBuffer.
ByteBuf is a data buffer encapsulated by Netty framework. It is different from position, limit, flip and other attributes and operations to control the reading and writing of ByteBuffer. ByteBuf assists the reading and writing of buffer through two position pointers, readIndex and writeIndex respectively.
The readIndex, writeIndex, and capacity variables have the following relationships:
0 <= readIndex <= writeIndex <= capacity Copy code
Implementation principle
When initializing ByteBuffer, the values of readIndex and writeIndex are 0 at first. As shown in the figure below:
After writing data, writeIndex will increase, as shown in the following figure:
After reading data, the readIndex will increase, but not exceed the writeIndex, as shown in the following figure:
After reading, the area from index 0 to readIndex is regarded as discarded bytes. You can call the discardReadBytes method to free up this space, which is similar to the compact() method of ByteBuffer to remove useless data and reuse the buffer. The following figure shows the situation after discardReadBytes is executed, which is equivalent to that the writable space becomes larger.
Use case for ByteBuf
In order to better understand ByteBuf, the following example is written:
public class ByteBufDemo { /** * @param args */ public static void main(String[] args) { // Create a buffer ByteBuf buffer = Unpooled.buffer(10); System.out.println("------------Initial buffer------------"); printBuffer(buffer); // Add some data to the buffer System.out.println("------------Add data to buffer------------"); String s = "love"; buffer.writeBytes(s.getBytes()); printBuffer(buffer); // Read data System.out.println("------------Read data------------"); while (buffer.isReadable()) { System.out.println(buffer.readByte()); } printBuffer(buffer); // Execute compact System.out.println("------------implement discardReadBytes------------"); buffer.discardReadBytes(); printBuffer(buffer); // Execute clear System.out.println("------------implement clear Empty buffer------------"); buffer.clear(); printBuffer(buffer); } /** * Print out ByteBuf information * * @param buffer */ private static void printBuffer(ByteBuf buffer) { System.out.println("readerIndex: " + buffer.readerIndex()); System.out.println("writerIndex: " + buffer.writerIndex()); System.out.println("capacity: " + buffer.capacity()); } } Copy code
Output results:
------------Initial buffer------------ readerIndex: 0 writerIndex: 0 capacity: 10 ------------Add data to buffer------------ readerIndex: 0 writerIndex: 4 capacity: 10 ------------Read data------------ 108 111 118 101 readerIndex: 4 writerIndex: 4 capacity: 10 ------------implement discardReadBytes------------ readerIndex: 0 writerIndex: 0 capacity: 10 ------------implement clear Empty buffer------------ readerIndex: 0 writerIndex: 0 capacity: 10 Process finished with exit code 0 Copy code
Comparing the ByteBuffer and ByteBuffer examples, we can see that Netty provides a more convenient tool to create ByteBuffer (unpooled). At the same time, it is no longer necessary to execute the flip() method to switch the read-write mode. ByteBuf is easier to use by contrast.
Three usage modes of ByteBuf
ByteBuf fer has three usage modes: Heap Buffer mode, Direct Buffer mode and Composite Buffer mode.
Heap buffer mode
Heap buffer mode is also called supporting array. Its data is stored in the heap space of the JVM, which is realized by storing the data in the array.
Advantages: data stored in JVM heap can be quickly created and released, and provides a method for fast data access;
Disadvantages: each time the data is transferred with I/O, the data needs to be copied to the direct buffer.
The following is a code example of a heap buffer:
public class ByteBufHeapBufferDemo { /** * @param args */ public static void main(String[] args) { // Create a heap buffer ByteBuf buffer = Unpooled.buffer(10); String s = "waylau"; buffer.writeBytes(s.getBytes()); // Check whether it is a support array if (buffer.hasArray()) { // Gets a reference to the support array byte[] array = buffer.array(); // Calculates the offset of the first byte int offset = buffer.readerIndex() + buffer.arrayOffset(); // Readable bytes int length = buffer.readableBytes(); printBuffer(array, offset, length); } } /** * Print the Buffer information * * @param buffer */ private static void printBuffer(byte[] array, int offset, int len) { System.out.println("array: " + array); System.out.println("array->String: " + new String(array)); System.out.println("offset: " + offset); System.out.println("len: " + len); } } Copy code
Output results:
array: [B@5b37e0d2 array->String: waylau offset: 0 len: 6 Process finished with exit code 0 Copy code
Direct buffer mode
The direct buffer belongs to the direct memory allocated outside the heap and will not occupy the space of the heap.
Advantages: when using socket to transmit data, the performance is very good, avoiding the process of copying data from JVM heap memory to direct buffer, and improving the performance.
Disadvantages: compared with heap buffers, direct buffers are more expensive to allocate memory space and free up.
For reading and writing involving a large amount of I/O data, it is recommended to use direct buffer. For the back-end business message encoding and decoding module, it is recommended to use heap buffer.
The following is an example of direct buffer Code:
public class ByteBufDirectBufferDemo { /** * @param args */ public static void main(String[] args) { // Create a direct buffer ByteBuf buffer = Unpooled.directBuffer(10); String s = "waylau"; buffer.writeBytes(s.getBytes()); // Check whether it is a support array // If it is not a supporting array, it is a direct buffer if (!buffer.hasArray()) { // Calculates the offset of the first byte int offset = buffer.readerIndex(); // Readable bytes int length = buffer.readableBytes(); // Get byte content byte[] array = new byte[length]; buffer.getBytes(offset, array); printBuffer(array, offset, length); } } /** * Print the Buffer information * * @param buffer */ private static void printBuffer(byte[] array, int offset, int len) { System.out.println("array: " + array); System.out.println("array->String: " + new String(array)); System.out.println("offset: " + offset); System.out.println("len: " + len); } } Copy code
Output results:
array: [B@6d5380c2 array->String: waylau offset: 0 len: 6 Process finished with exit code 0 Copy code
Compound buffer mode
Composite buffers are Netty specific buffers. Essentially, it is similar to providing a combined view of one or more bytebufs. Different types of bytebufs can be added and deleted as needed.
Advantages: it provides an access method, allowing users to freely combine multiple bytebufs, avoiding copying and allocating new buffers.
Disadvantages: access to its supporting array is not supported. Therefore, if you want to access, you need to copy the contents into heap memory before accessing.
The following example is a composite buffer, which combines a heap buffer and a direct buffer without any replication process, and only creates a view.
public class ByteBufCompositeBufferDemo { /** * @param args */ public static void main(String[] args) { // Create a heap buffer ByteBuf heapBuf = Unpooled.buffer(3); String way = "way"; heapBuf.writeBytes(way.getBytes()); // Create a direct buffer ByteBuf directBuf = Unpooled.directBuffer(3); String lau = "lau"; directBuf.writeBytes(lau.getBytes()); // Create a composite buffer CompositeByteBuf compositeBuffer = Unpooled.compositeBuffer(10); compositeBuffer.addComponents(heapBuf, directBuf); // Add buffer to match buffer // Check whether it is a support array // If it is not a supporting array, it is a composite buffer if (!compositeBuffer.hasArray()) { for (ByteBuf buffer : compositeBuffer) { // Calculates the offset of the first byte int offset = buffer.readerIndex(); // Readable bytes int length = buffer.readableBytes(); // Get byte content byte[] array = new byte[length]; buffer.getBytes(offset, array); printBuffer(array, offset, length); } } } /** * Print the Buffer information * * @param buffer */ private static void printBuffer(byte[] array, int offset, int len) { System.out.println("array: " + array); System.out.println("array->String: " + new String(array)); System.out.println("offset: " + offset); System.out.println("len: " + len); } } Copy code
Output results:
array: [B@4d76f3f8 array->String: way offset: 0 len: 3 array: [B@2d8e6db6 array->String: lau offset: 0 len: 3 Process finished with exit code 0 Copy code
CompositeByteBuf is a virtual buffer used to display multiple buffers as a single merge buffer, similar to views in a database.
summary
Through the above introduction to ByteBuf, I believe our friends have a certain understanding of the principle of ByteBuf. In the next section, we continue to delve into the source code of Netty.
ending
I am a yard farmer who is being hit and still trying to move forward. If the article is helpful to you, remember to praise and pay attention, thank you!
Author: first love
Link: https://juejin.cn/post/6995058704714301476
Source: Nuggets
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.