netty核心类-缓冲区ByteBuf(转载自https://blog.csdn.net/u010853261/article/details/53690780)

[netty核心类]–缓冲区ByteBuf
本文主要包括以下内容:
1)ByteBuf的三种类型:heapBuffer(堆缓冲区)、directBuffer(直接缓冲区)以及Composite Buffer(复合缓冲区)。

2)ByteBuf的工作原理。

3)ByteBuf与JDK中ByteBuffer的区别以及对比

4)ByteBuf的引用计数器实现类AbstractReferenceCountedByteBuf分析。

5)UnpooledHeapByteBuf 基于堆内存缓冲器的源码分析

6)PooledDirectByteBuf 源码分析

  1. netty中ByteBuf的优势
    缓冲区是不同的通道之间传递数据的中介,JDK中的ByteBuffer操作复杂,而且没有经过优化,所以在netty中实现了一个更加强大的缓冲区 ByteBuf 用于表示字节序列。ByteBuf在netty中是通过Channel传输数据的,新的设计解决了JDK中ByteBuffer中的一些问题。

netty中ByteBuf的缓冲区的优势:
(1)可以自定义缓冲区的类型;

(2)通过内置的复合缓冲类型实现零拷贝;

(3)不需要调用flip()函数切换读/写模式

(4)读取和写入的索引分开了,不像JDK中使用一个索引

(5)引用计数(referenceCounting的实现原理?)

扫描二维码关注公众号,回复: 2848511 查看本文章

(6) Pooling池

2.netty中的ByteBuf的类型:
JDK中的Buffer的类型 有heapBuffer和directBuffer两种类型,但是在netty中除了heap和direct类型外,还有composite Buffer(复合缓冲区类型)。

(1)Heap Buffer 堆缓冲区
这是最常用的类型,ByteBuf将数据存储在JVM的堆空间,通过将数据存储在数组中实现的。
1)堆缓冲的优点是:由于数据存储在JVM的堆中可以快速创建和快速释放,并且提供了数组的直接快速访问的方法。

2)堆缓冲缺点是:每次读写数据都要先将数据拷贝到直接缓冲区再进行传递。

(2)Direct Buffer 直接缓冲区
Direct Buffer在堆之外直接分配内存,直接缓冲区不会占用堆的容量。

(1)Direct Buffer的优点是:在使用Socket传递数据时性能很好,由于数据直接在内存中,不存在从JVM拷贝数据到直接缓冲区的过程,性能好。

(2)缺点是:因为Direct Buffer是直接在内存中,所以分配内存空间和释放内存比堆缓冲区更复杂和慢。

虽然netty的Direct Buffer有这个缺点,但是netty通过内存池来解决这个问题。直接缓冲池不支持数组访问数据,但可以通过间接的方式访问数据数组:

ByteBuf directBuf = Unpooled.directBuffer(16);
if(!directBuf.hasArray()){
int len = directBuf.readableBytes();
byte[] arr = new byte[len];
directBuf.getBytes(0, arr);
}
1
2
3
4
5
6
但是上面的操作太过复杂,所以在使用时,建议一般是用heap buffer。

不过对于一些IO通信线程中读写缓冲时建议使用DirectByteBuffer,因为这涉及到大量的IO数据读写。对于后端的业务消息的编解码模块使用HeapByteBuffer。

(3)Composite Buffer 复合缓冲区
这个是netty特有的缓冲类型。复合缓冲区就类似于一个ByteBuf的组合视图,在这个视图里面我们可以创建不同的ByteBuf(可以是不同类型的)。 这样,复合缓冲区就类似于一个列表,我们可以动态的往里面添加和删除其中的ByteBuf,JDK里面的ByteBuffer就没有这样的功能。

Netty提供了Composite ByteBuf来处理复合缓冲区。例如:一条消息由Header和Body组成,将header和body组装成一条消息发送出去。下图显示了Composite ByteBuf组成header和body:
这里写图片描述

如果使用的是JDK的ByteBuffer就不能简单的实现,只能通过创建数组或则新的ByteBuffer,再将里面的内容复制到新的ByteBuffer中,下面给出了一个CompositeByteBuf的使用示例:

//组合缓冲区
CompositeByteBuf compBuf = Unpooled.compositeBuffer();
//堆缓冲区
ByteBuf heapBuf = Unpooled.buffer(8);
//直接缓冲区
ByteBuf directBuf = Unpooled.directBuffer(16);
//添加ByteBuf到CompositeByteBuf
compBuf.addComponents(heapBuf, directBuf);
//删除第一个ByteBuf
compBuf.removeComponent(0);
Iterator iter = compBuf.iterator();
while(iter.hasNext()){
System.out.println(iter.next().toString());
}

//使用数组访问数据
if(!compBuf.hasArray()){
int len = compBuf.readableBytes();
byte[] arr = new byte[len];
compBuf.getBytes(0, arr);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
3.ByteBuf是如何工作的:
ByteBuf是一个抽象类,内部全部是抽象的函数接口,AbstractByteBuf这个抽象类基本实现了ByteBuf,下面我们通过分析AbstractByteBuf里面的实现来分析ByteBuf的工作原理。

ByteBuf都是基于字节序列的,类似于一个字节数组。在AbstractByteBuf里面定义了下面5个变量:

//源码
int readerIndex; //读索引
int writerIndex; //写索引
private int markedReaderIndex;//标记读索引
private int markedWriterIndex;//标记写索引
private int maxCapacity;//缓冲区的最大容量
1
2
3
4
5
6

ByteBuf 与JDK中的 ByteBuffer 的最大区别之一就是:
(1)netty的ByteBuf采用了读/写索引分离,一个初始化的ByteBuf的readerIndex和writerIndex都处于0位置。

(2)当读索引和写索引处于同一位置时,如果我们继续读取,就会抛出异常IndexOutOfBoundsException。

(3)对于ByteBuf的任何读写操作都会分别单独的维护读索引和写索引。maxCapacity最大容量默认的限制就是Integer.MAX_VALUE。

1.随机访问索引
ByteBuf提供读/写索引,从0开始的索引,第一个字节索引是0,最后一个字节的索引是capacity-1,下面给出一个示例遍历ByteBuf的字节:

public static void main(String[] args) {
//创建一个16字节的buffer,这里默认是创建heap buffer
ByteBuf buf = Unpooled.buffer(16);
//写数据到buffer
for(int i=0; i<16; i++){
buf.writeByte(i+1);
}
//读数据
for(int i=0; i

猜你喜欢

转载自blog.csdn.net/m0_37556444/article/details/81558863