ByteBuf结构
ByteBuf结构示意图:
根据示意图可以看出:
- ByteBuf是一个字节容器,容器整体可分为四个部分:
- 已废弃字节部分,此部分的数据是无效的;
- 可读字节部分,此部分的数据是ByteBuf的主体数据,从ByteBuf读取的数据来自此部分;
- 可写字节部分,所有向ByteBuf写入的数据都会保存在此部分;
- 剩余可扩容字节部分,表示ByteBuf还能扩容多少字节;
- 其中前三部分是通过两个指针来划分开的,一个是读指针(readerIndex)另一个是写指针(writerIndex),还有一个变量容量(capacity)用以表示ByteBuf底层内存的总量;
- 从ByteBuf中往外读取数据时,readerIndex自增1,ByteBuf总共有(readerIndex - writerIndex)个可读字节,因此readerIndex 与readerIndex相等时是无法读取数据的;
- 写数据是从writerIndex指向的部分开始写入,每写入一个字节writerIndex自增1,直到与capacity相等时表示ByteBuf已经不可写入;
- maxCapacity表示ByteBuffer可扩容到的字节上限,当向ByteBuf中写入数据时,如果容量不够则会进行扩容,当capacity比maxCapacity大时则会报错;
与容量相关的API
常见API如下:
API名称 | 功能介绍 |
---|---|
capacity() | 表示ByteBuf底层占用了多少字节内存; |
maxCapacity() | 表示ByteBuf底层能够占用的最大字节数; |
readableBytes | 表示当前ByteBuf可读的字节数,与(writerIndex - readerIndex)等值; |
isReadable() | 如果writableIndex与readableIndex等值,则不可读,isReadable返回fales; |
writableBytes() | 表示ByteBuf当前可写的字节数,等值于capacity - writerIndex; |
isWriterable() | 如果capacity与writerIndex等值,则为不可写状态,isWritable()返回false; |
maxwritableBytes() | maxwritableBytes()表示可写的最大字节数,等值于maxCapacity - writerIndex; |
与指针相关的API
常见API如下:
API名称 | 功能介绍 |
---|---|
readerIndex() | 表示获取当前的读指针; |
readerIndex(int) | 表示设置读指针的位置; |
writerIndex() | 表示获取当前的写指针; |
writerIndex(int) | 表示设置写指针的位置; |
markReaderIndex() | 表示保存读指针的位置; |
reasetReaderIndex() | 表示将当前读指针的位置恢复到上次保存的位置; |
markWriterIndex() | 表示保存写指针的位置; |
resetWriterIndex() | 表示将当前写指针的位置恢复到上次保存的位置; |
读写API
常见API如下:
API名称 | 功能介绍 |
---|---|
writeBytes(byte[]) | 表示将参数数据写入到ByteBuf中; 通常情况下参数数组与可写入数据大小相同; |
readerBytes(byte[]) | 表示将ByteBuf中的数据写入到参数数组中; 通常情况下参数数组与可读数据长度相同; |
writeByte(byte) | 表示写入一个Byte到ByteBuf中; 与之类似的还有writeLong()、writeDouble()等等; |
readByte(byte) | 表示从ByteBuf读取一个Byte数据; 与之类似的还有readLong()、readDouble()等; |
release() | 表示使得引用计数+1; |
retain() | 表示使得引用计数-1; |
slice() | 返回一个ByteBuf,其中的数据为原ByteBuf从readerIndex至writerIndex之间的数据,且返回的ByteBuf的maxCapacity为原ByteBuf的readableBytes(); 向返回的ByteBuf与原有ByteBuf内存共享; 不影响原有ByteBuf的指针; 不会影响原有ByteBuf的引用数; |
duplicate() | 返回一个ByteBuf,返回的ByteBuf与原ByteBuf的数据、指针相同; 向返回的ByteBuf与原有ByteBuf内存共享; 不影响原有ByteBuf的指针; 不会影响原有ByteBuf的引用数; |
copy() | 返回一个ByteBuf,返回的ByteBuf与原ByteBuf完全相同,向返回的ByteBuf写入数据并不会影响原有ByteBuf; 不影响原有ByteBuf的指针; 不会影响原有ByteBuf的引用数; |
retainedSlice() | 表示在原有slice()方法的基础上,增加原有ByteBuf的引用数; |
retainedDuplicate() | 表示在原有duplicate()方法的基础上,增加原有ByteBuf的引用数; |
值的注意的是,get/set操作并不会改变读写指针,而read/write操作则会改变读写指针。
Netty使用了堆外内存,堆外内存不归JVM管理,也就是意味着申请的内存无法被GC回收,需要手动进行内存回收的操作。
其中,Netty中的ByteBuf是通过引用计数进行管理的,如果某个ByteBuf的引用计数降为0时,该ByteBuf将直接回收。
常见易错点
有如下常见易错的地方:
- 多次释放
- 不释放造成内存泄漏
为避免上述情况发生,只要在增加计数引用地方(包括ByteBuf的创建和手动调用retain()方法),就必须调用release()方法。
示例代码
示例代码ByteBufTest.java
/**
* @program: learnnetty
* @description: ByteBuf测试
* @create: 2020-05-05 16:39
**/
public class ByteBufTest {
public static void main(String[] args) {
ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(9, 100);
printDetails("allocate ByteBuf(9, 100)", buffer);
buffer.writeBytes(new byte[]{1,2,3,4});
printDetails("writeBytes{1,2,3,4}", buffer);
buffer.writeInt(12);
printDetails("writeInt(12)", buffer);
buffer.writeBytes(new byte[]{12});
printDetails("writeBytes(12)", buffer);
buffer.writeBytes(new byte[]{24});
printDetails("writeBytes(24)", buffer);
System.out.println("getByte(3):" + buffer.getByte(3));
System.out.println("getShort(3):" + buffer.getShort(3));
System.out.println("getInt(3):" + buffer.getInt(3));
printDetails("getXXX()", buffer);
buffer.setByte(buffer.readableBytes() + 1, 0);
printDetails("setXXX()", buffer);
byte[] dst = new byte[buffer.readableBytes()];
buffer.readBytes(dst);
printDetails("readBytes(" + dst.length + ")", buffer);
}
private static void printDetails(String title, ByteBuf buffer){
System.out.println("*************" + title + "*************");
System.out.println("capacity:" + buffer.capacity());
System.out.println("maxCapacity:" + buffer.maxCapacity());
System.out.println("readerIndex:" + buffer.readerIndex());
System.out.println("readableBytes:" + buffer.readableBytes());
System.out.println("isReadable:" + buffer.isReadable());
System.out.println("writerIndex:" + buffer.writerIndex());
System.out.println("writableBytes:" + buffer.writableBytes());
System.out.println("isWritable:" + buffer.isWritable());
System.out.println("maxWritableBytes:" + buffer.maxWritableBytes());
System.out.println("**************************");
}
}
输出:
*************allocate ByteBuf(9, 100)*************
capacity:9
maxCapacity:100
readerIndex:0
readableBytes:0
isReadable:false
writerIndex:0
writableBytes:9
isWritable:true
maxWritableBytes:100
**************************
*************writeBytes{1,2,3,4}*************
capacity:9
maxCapacity:100
readerIndex:0
readableBytes:4
isReadable:true
writerIndex:4
writableBytes:5
isWritable:true
maxWritableBytes:96
**************************
*************writeInt(12)*************
capacity:9
maxCapacity:100
readerIndex:0
readableBytes:8
isReadable:true
writerIndex:8
writableBytes:1
isWritable:true
maxWritableBytes:92
**************************
*************writeBytes(12)*************
capacity:9
maxCapacity:100
readerIndex:0
readableBytes:9
isReadable:true
writerIndex:9
writableBytes:0
isWritable:false
maxWritableBytes:91
**************************
*************writeBytes(24)*************
capacity:16
maxCapacity:100
readerIndex:0
readableBytes:10
isReadable:true
writerIndex:10
writableBytes:6
isWritable:true
maxWritableBytes:90
**************************
getByte(3):4
getShort(3):1024
getInt(3):67108864
*************getXXX()*************
capacity:16
maxCapacity:100
readerIndex:0
readableBytes:10
isReadable:true
writerIndex:10
writableBytes:6
isWritable:true
maxWritableBytes:90
**************************
*************setXXX()*************
capacity:16
maxCapacity:100
readerIndex:0
readableBytes:10
isReadable:true
writerIndex:10
writableBytes:6
isWritable:true
maxWritableBytes:90
**************************
*************readBytes(10)*************
capacity:16
maxCapacity:100
readerIndex:10
readableBytes:0
isReadable:false
writerIndex:10
writableBytes:6
isWritable:true
maxWritableBytes:90
**************************