作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题
代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬。码炫课堂的个人空间-码炫码哥个人主页-面试,源码等
释放21集全网最深ConcurrentHashMap的vip视频,复现每一行源码
回答
ByteBuf
是Netty 数据传输的载体,是字节缓冲区,用于在 I/O 操作中存储字节数据。在 Netty 中,数据的读写都是以 ByteBuf
为单位进行交互的。
它与 Java NIO 中的 ByteBuffer 存在以下几个区别:
- 读写索引
ByteBuf
:提供了两个独立的索引 —— 一个用于读操作的 readerIndex,另一个用于写操作 writerIndex。这使得ByteBuf
同时进行读写操作变得更加方便,无需在读和写模式之间切换。ByteBuffer
:只有一个位置指针,用于读写操作。在进行读写操作切换时需要调用flip()
,这使得ByteBuffer
使用起来不如 ByteBuf 灵活。
- 引用计数
ByteBuf
:支持引用计数,这是的 ByteBuf 可以更加有效地管理和回收内存。Netty 可以根据引用计数来判断一个ByteBuf
是否不再被使用,当引用计数为 0 时,它可以被自动释放,这有助于防止内存泄露。ByteBuffer
:不支持引用计数,内存管理完全由 JVM 的垃圾回收器负责,这可能会导致内存泄露。
- 可扩展性和池化
ByteBuf
:可以根据需要动态调整容量,更加灵活,同时 Netty 还提供了字节缓冲区池,可以重用ByteBuf
实例,降低内存分配和回收的开销。ByteBuffer
:一旦创建,其容量就固定了,不能动态扩展或缩小,而且它不支持池化,在内存使用方面不及ByteBuf
。
- API的用户友好性
ByteBuf
:提供了更加丰富和友好的 API,使得对其的操作更加容易和直观。ByteBuffer
:其 API 相对较为简单和有限,某些操作可能需要更复杂的步骤和更多的代码。
扩展
ByteBuf内部结构
我们首先看 ByteBuf 的内部结构:
从 ByteBuf 的内部结构可以看出,它包含有三个指针:
- readerIndex:读指针
- writerIndex:写指针
- maxCapacity:最大容量
三个指针将整个 ByteBuf 分为四个部分:
- 废弃字节:表示已经丢弃的无效字节,我们可以调用
discardReadBytes()
释放这部分空间。 - 可读字节:表示可以从 ByteBuf 中读取到的数据,这部分内容等于 writerIndex - readerIndex。readerIndex 随着我们读取 ByteBuf 中的数据而递增,当从 ByteBuf 中读取 N 个字节, readerIndex 就会自增 N,直到 readerIndex = writerIndex 时,就表示 ByteBuf 不可读。
- 可写字节:表示可以向 ByteBuf 可写入的字节。writerIndex 也是随着我们向 ByteBuf 中写入数据而自增,当想 ByteBuf 中写入 N 个字节,writerIndex 就会自增 N,当 writerIndex 超过 capacity 时,就需要扩容了。
- 可扩容字节:表示 ByteBuf 最多可扩容多少字节 。当向 ByteBuf 写入的数据超过了 capacity 时,就会触发扩容,但是最多可扩容到 maxCapacity ,超过时就会报错。
从这里就可以看出,ByteBuf 很好地解决了原生 NIO ByteBuffer 的不可扩容及读写模式切换的问题。Netty 为什么要设计两个指针呢?主要是为了能够更加高效、更加灵活地处理数据,有两个指针有如下几个优势:
- 读写分离:使用两个指针可以将读写两个操作进行有效地分离,而不会相互影响。这使得在同一时间,我们可以在不破坏数据完整性的前提下,进行同时的读取和写入操作。
- 更加灵活:两个指针意味着互相独立,我们可以在不影响读指针的情况下,自由地移动写指针,或者在不影响写指针的情况下,自由地移动读指针,这种分离的设计为数据处理提哦乖乖女了更大的灵活性。
ByteBuf 索引变化
清楚了 ByteBuf 的内部结构,我们还需要了解它内部索引的变化情况。
- 初始分配:这个时候 readerIndex = writerIndex = 0
- 当我们向 ByteBuf 中写入 N 个字节后,readerIndex = 0,writerIndex = N
- 当我们从 ByteBuf 中读取 M(M < N)个字节后,readerIndex = M,writerIndex = N
- 当我们继续往 ByteBuf 中写入数据时ÿ