伊成博客
伊成博客

浅谈ByteBuffer和ByteBuf的区别

写在前面

最近这段时间接触到了netty,在做消息的编解码的过程中认识了bytebuf初次见面尚未深入认识,看见bytebuf的第一眼让我一下就联想到了bytebuffer这‘家伙’。

进过一段时间的使用,今天写一篇博客总结总结!

ByteBuffer

bytebuffer 是Java NIO里面提供的字节容器。有一个指针用于处理读写操作,每次读写的时候都需要调用flip()或是clear()方法,不然将会报异常。

部分源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
*
*
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @since 1.4
*/

public abstract class ByteBuffer
extends Buffer
implements Comparable<ByteBuffer>
{

// These fields are declared here rather than in Heap-X-Buffer in order to
// reduce the number of virtual method invocations needed to access these
// values, which is especially costly when coding small buffers.
//
final byte[] hb; // Non-null only for heap buffers
final int offset;
boolean isReadOnly; // Valid only for heap buffers

// Creates a new buffer with the given mark, position, limit, capacity,
// backing array, and array offset
//
ByteBuffer(int mark, int pos, int lim, int cap, // package-private
byte[] hb, int offset)
{
super(mark, pos, lim, cap);
this.hb = hb;
this.offset = offset;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class Buffer {

/**
* The characteristics of Spliterators that traverse and split elements
* maintained in Buffers.
*/
static final int SPLITERATOR_CHARACTERISTICS =
Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;

// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
属性 描述
mark 调用mark()方法的话,mark值将存储当前position的值,等下次调用reset()方法时,会设定position的值为之前的标记值
position 用来表示bytes的容量,那么可以想像capacity就等于bytes.size(),此值在初始化bytes后,是不可变的
limit 用来表示bytes实际装了多少数据,可以容易想像得到limit <= capacity,此值是可灵活变动的
capacity 用来表示在哪个位置开始往bytes写数据或是读数据,此值是可灵活变动的

他们之间的关系:mark <= position <= limit <= capacity

创建一个bytebuffer

ByteBuffer bf = ByteBuffer.allocate(10);

position,limit和capacity图解如下:

image

tips:创建一个bytebuffer有两个方法

1
2
public static ByteBuffer allocate(int capacity)  
public static ByteBuffer allocateDirect(int capacity)

allocate和allocateDirect的区别,可以参考以下博文

https://blog.51cto.com/xingej/1967948
https://www.jianshu.com/p/03054776bc60

写入数据到bytebuffer

bf.put((byte)’H’)
.put((byte)’e’)
.put((byte)’l’)
.put((byte)’l’)
.put((byte)’o’)

在操作bytebuffer的时候,每次往里面写入一个byte,position则会后移一位。

image

使用flip() 刷新缓冲区为 读模式

bf.flip()

bytebuffer有两种模式,分别是写模式和读模式,这两种模式通过使用flip方法进行模式。

如上将缓冲区切换为读模式,则position变成了初值位置0,而limit变成了写模式下position位置。
image

读取数据

bf.get()

调用get()获取缓冲区中的一个byte。

清除缓冲区

bf.clear()

tips: 这个方法简单理解就是复位(Reset) 但不会清除数据(position=0, limit=capacity)

ByteBuf

bytebuf 是Netty里的封装的数据缓存区,区别于bytebuffer里需要position、limit、capacity等属性来操作bytebuffer数据读写,而 bytebuf 里面则是通过 两个指针协助缓存区的读写操作,分别为 readIndex 和 writerIndex 。

在创建bytebuf 的时候,readIndex 和 writerIndex 的值都是0,但随着有数据被写入 writerIndex会增加,读取数据的时候 readIndex也会增加, 但是readIndex 不会超过 writerIndex。

创建一个bytebuf

ByteBuf bf= Unpooled.buffer(10,100)

image

write:写入N个字节之后ByteBuf

image

read:读取M个字节后(M<N)
image

ByteBuffer和ByteBuf的区别

  • Netty的ByteBuf采用了读写索引分离的策略(readerIndex与writerIndex),一个初始化(里面尚未有任何数据)的ByteBuf的readerIndex与writerIndex值都为0

  • 当读索引与写索引处于同一个位置时,如果继续读取,那么就会抛出IndexOutOfBoundsException。

  • ByteBuffer只有一个标识位置的指针,读写的时候需要手动的调用flip()和rewind()等,否则很容易导致程序处理失败。而ByteBuf有两个标识位置的指针,一个写writerIndex,一个读readerIndex,读写的时候不需要调用额外的方法。

  • ByteBuffer必须自己长度固定,一旦分配完成,它的容量不能动态扩展和收缩;ByteBuf默认容器大小为256,支持动态扩容,在允许的最大扩容范围内(Integer.MAX_VALUE)。

  • NIO的SocketChannel进行网络读写时,操作的对象是JDK标准的java.nio.byteBuffer。由于Netty使用统一的ByteBuf替代JDK原生的java.nio.ByteBuffer,所以ByteBuf中定义了ByteBuffer nioBuffer()方法将ByteBuf转换成ByteBuffer。

支付宝打赏 微信打赏

如果本文对你有所帮助,请打赏 1元就足够感动我 :)