答:
在 Java NIO 编程中,Java 提供了 ByteBuffer 作为字节缓冲区类型(缓冲区可以理解为一段内存区域),来表示一个连续的字节序列。
Netty 中并没有使用 Java 的 ByteBuffer,而是使用了新的缓冲类型 ByteBuf,特性如下:
允许自定义缓冲类型
复合缓冲类型中内置的透明的零拷贝实现
开箱即用的动态缓冲类型,具有像 StringBuffer 一样的动态缓冲能力
不再需要调用 flip() 方法
Java 的 ByteBuffer 类中,需要使用 flip() 来进行读写两种模式的切换
正常情况下具有比 ByteBuffer 更快的响应速度
Java 中的 ByteBuffer:
主要需要注意有 3 个属性:position、limit、capacity
假如说数组容量是 10,那么三个值初始值为:
position = 0
limit = 10
capacity = 10
假如写入 4 个字节的数据,此时三个值如下:
position = 4
limit = 10
capacity = 10
如果切换到读取数据模式(使用 flip()
),会改变上边的三个值,会从 position 的位置开始读取数据到 limit 的位置
position = 0
limit = 4
capacity = 10
Netty 中的 ByteBuf:
ByteBuf 主要使用两个指针来完成缓冲区的读写操作,分别是: readIndex
和 writeIndex
ByteBuf 的使用:
public static void main(String[] args) {
ByteBuf buffer = Unpooled.buffer(10);
System.out.println("----------初始化ByteBuf----------");
printByteBuffer(buffer);
System.out.println("----------ByteBuf写入数据----------");
String str = "hello world!";
buffer.writeBytes(str.getBytes());
printByteBuffer(buffer);
System.out.println("----------ByteBuf读取数据----------");
while (buffer.isReadable()) {
System.out.print((char)buffer.readByte());
}
System.out.println();
printByteBuffer(buffer);
System.out.println("----------ByteBuf释放无用空间----------");
buffer.discardReadBytes();
printByteBuffer(buffer);
System.out.println("----------ByteBuf清空----------");
buffer.clear();
printByteBuffer(buffer);
}
private static void printByteBuffer(ByteBuf buffer) {
System.out.println("readerIndex:" + buffer.readerIndex());
System.out.println("writerIndex:" + buffer.writerIndex());
System.out.println("capacity:" + buffer.capacity());
}
/**输出**/
----------初始化ByteBuf----------
readerIndex:0
writerIndex:0
capacity:10
----------ByteBuf写入数据----------
readerIndex:0
writerIndex:12
capacity:64
----------ByteBuf读取数据----------
hello world!
readerIndex:12
writerIndex:12
capacity:64
----------ByteBuf释放无用空间----------
readerIndex:0
writerIndex:0
capacity:64
----------ByteBuf清空----------
readerIndex:0
writerIndex:0
capacity:64
ByteBuf 的 3 种使用模式:
ByteBuf 共有 3 种使用模式:
堆缓冲区模式(Heap Buffer)
堆缓冲区模式又称为 “支撑数据”,其数据存放在 JVM 的堆空间
优点:
缺点:
直接内存比堆内存快在了哪里?
问题中已经讲过)创建代码:
ByteBuf buffer = Unpooled.buffer(10);
直接缓冲区模式(Direct Buffer)
直接缓冲区模式属于堆外分配的直接内存,不占用堆的容量
优点:
缺点:
创建代码:
ByteBuf buffer = Unpooled.directBuffer(10);
复合缓冲区模式(Composite Buffer)
本质上类似于提供一个或多个 ByteBuf 的组合视图
优点:
缺点:
创建代码:
public static void main(String[] args) {
// AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test.class);
// 创建一个堆缓冲区
ByteBuf heapBuf = Unpooled.buffer(2);
String str1 = "hi";
heapBuf.writeBytes(str1.getBytes());
// 创建一个直接缓冲区
ByteBuf directBuf = Unpooled.directBuffer(5);
String str2 = "nihao";
directBuf.writeBytes(str2.getBytes());
// 创建一个复合缓冲区
CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer(10);
compositeByteBuf.addComponents(heapBuf, directBuf);
// 检查是否支持支撑数组,发现并不支持
if (!compositeByteBuf.hasArray()) {
for (ByteBuf buf : compositeByteBuf) {
// 第一个字节偏移量
int offset = buf.readerIndex();
// 总共数据长度
int length = buf.readableBytes();
byte[] bytes = new byte[length];
// 不支持访问支撑数组,需要将内容复制到堆内存中,即 bytes 数组中,才可以进行访问
buf.getBytes(offset, bytes);
printByteBuffer(bytes, offset, length);
}
}
}
private static void printByteBuffer(byte[] array, int offset, int length) {
System.out.println("array:" + array);
System.out.println("array->String:" + new String(array));
System.out.println("offset:" + offset);
System.out.println("len:" + length);
}
/**输出**/
array:[B@4f8e5cde
array->String:hi
offset:0
len:2
array:[B@504bae78
array->String:nihao
offset:0
len:5
答:
ByteBuf 的分配接口定义在了 ByteBufAllocator
中,他的直接抽象类是 AbstractByteBufAllocator
,而 AbstractByteBufAllocator
有两种实现:PooledByteBufAllocator
和 UnpooledByteBufAllocator