ByteBuf就是一个Byte数组缓存区
通过readerIdnex和writerIndex来协助读写操作 还有个capacity指针表明容量
会自动进行动态扩容
Discardable Bytes
相比其他Java对象,缓冲区的分配和释放是一个耗时的操作,所以需要尽量重用.可以使用discardReadBytes将数组往已读部分移动,从而复用已读部分,增大可写空间.但这存在内存复制的现象,所以频繁使用会导致性能下降.
clear :将缓冲区填充为Null(0x00) 同时还原reader/writerIndex
mark对应指针 记录该指针当前位置 之后再reset可以将指针移动到该位置 比如预留空间给之后数组长度,之后回滚到这记录
各种查找操作
复制ByteBuf duplicate(共享空间不共享指针) copy(空间指针都不共享 就是复制)
通过Nio的SocketChannel进行网络读写时,操作的对象时JD看到ByteBuffer 所以要将ByteBuf转化为ByteBuffer, nioBuffer() //int index int length 支持随机读写
ByteBuf 根据内存位置可以分为两大类:堆内存和直接内存
直接内存:在堆外进行内存回收 所以少从直接缓存和堆内缓存之间的复制,少了Java堆对应的复制,也就是所谓的零拷贝的一种,但不受到JVM GC 管理
经验表面: I/O通信读写缓存使用直接内存 后台业务消息的编解码使用堆内存性能最优
根据内存回收也可以分为两类 : 基于对象池和普通ByteBuf
基于对象池可以使用内存池重用ByteBuf对象
PooledByteBuf内存池(堆内)
实现类 PoolArena(Arena指一大块区域)
由多个Chunk组成,每个Chunk由一个或者多个Page构成 Chunk中Page会给构建成一个二叉树,根为所有Page的内存,当树的一个节点被分配出去后,节点会被标记为已分配,之后对这个节点及其子节点的请求就会被忽略.请求分配就只能找其他节点.
对于小于Page大小的请求 会将一个Page进行拆分(按请求大小等分),之后只能再将其他部分分配给等大小的请求. Page使用状态通过long数组维护 0表示未占用 1占用
PooledDirectByteBuf 实现类似 ,唯一不同就是分配和创建策略不同
第二种零拷贝: 可以将多个ByteBuf统一封装后向外提供一个ByteBuf不需要复制.
第三种零拷贝:直接将文件缓冲区的内容直接发送到Channel(网卡 大概是).
ByteBuf的释放
1.基于内存池的请求ByteBuf
由NioEventLoop分配,需要在ChannelInboundHandler后释放 此处释放指释放给内存池
()继承自SimpleChannelInboundHandler可以自动释放
()调用ctx.fireChannelRead(msg);将请求消息向后执行 直到Tail释放请求
非内存池请求ByteBuf也会释放
响应ByteBuf
ctx.writeAndFulsh.中Netty会在消息发送完成后协助释放内存,Heap内存会转化成Direct 并释放Heap内存 Direct会在消息完整写到SocketChannel后释放
所以调用了writeAndFulsh或者fulsh就不用业务释放