Netty ByteBuf和Nio ByteBuffer
参考https://blog.csdn.net/jeffleo/article/details/69230112
一、简介
Netty中引入了ByteBuf,它相对于ByteBuffer来说,带来了很多便捷性和创新的地方,使得程序员更简单得进行网络编程
二、ByteBuffer的缺点和ByteBuf的改进
下面我们从几个点来分别讲解:
1. 扩容
ByteBuffer:
ByteBuffer缓冲区的长度固定,分多了会浪费内存,分少了存放大的数据时会索引越界,所以使用ByteBuffer时,为了解决这个问题,我们一般每次put操作时,都会对可用空间进行校检,如果剩余空间不足,需要重新创建一个新的ByteBuffer,然后将旧的ByteBuffer复制到新的ByteBuffer中去。
ByteBuf:
而ByteBuf则对其进行了改进,它会自动扩展,具体的做法是,写入数据时,会调用ensureWritable方法,传入我们需要写的字节长度,判断是否需要扩容:
public ByteBuf ensureWritable(int minWritableBytes) {
if(minWritableBytes < 0) {
throw Exception
} else if(minWritableBytes <= this.writableBytes()) {
return this;
} else if(minWritableBytes > this.maxCapacity - this.writerIndex) {
throw Exception
} else {
//扩容
int newCapacity = this.calculateNewCapacity(this.writerIndex + minWritableBytes);
this.capacity(newCapacity);
return this;
}
}
private int calculateNewCapacity(int minNewCapacity) {
int maxCapacity = this.maxCapacity;
int threshold = 4194304; //4mb阀值
if(minNewCapacity == 4194304) {//如果新容量为阀值,直接返回
return 4194304;
} else {
int newCapacity;
if(minNewCapacity > 4194304) {//如果传入的新容量大于阀值,进行计算
newCapacity = minNewCapacity / 4194304 * 4194304;
if(newCapacity > maxCapacity - 4194304) {//如果大于最大容量,新容量为最大容量
newCapacity = maxCapacity;
} else {//否则新容量 + 阀值 4mb,按照阀值扩容
newCapacity += 4194304;
}
return newCapacity;
} else {//如果小于阀值,则以64为计数倍增,知道倍增的结果>=需要的容量值
for(newCapacity = 64; newCapacity < minNewCapacity; newCapacity <<= 1) {
;
}
return Math.min(newCapacity, maxCapacity);
}
}
}
这里要解释的地方:
1. 当申请的新空间大于阀值时,采用每次步进4MB的方式进行扩张内存,而不是倍增,因为这会造成内存膨胀和浪费
2. 而但申请的新空间小于阀值时,则以64为基数进行倍增而不是步进,因为当内存比较小的时候,倍增是可以接受的(64 -> 128 和 10Mb -> 20Mb相比)
2. 位置指针
ByteBuffer:
ByteBuffer中只有一个位置指针position(ByteBuf有两个),所以需要我们手动得调用flip等方法,例如:
ByteBuffer buffer = ByteBuffer.allocate(88);
String value = "我的博客";
buffer.put(value.getBytes());
buffer.flip();
byte[] array = new byte[buffer.remaining()];
buffer.get(array);
String decodeValue = new String(array);
ByteBuffer中会有三个下标,初始位置0,当前位置positon,limit位置,初始时,position为0,limit为Buffer数组末尾
调用buffer.put(value.getBytes())后:
不调用flip:
从缓冲区读取的是position — limit位置的数据,明显不是我们要的
调用flip:
会将limit设置为position,position设置为0,,此时读取的数据
ByteBuf:
ByteBuf中使用两个指针,readerIndex,writerIndex来指示位置,初始时readrIndex = writerIndex = 0,当写入数据后:
writerIndex — capacity:可写容量
readerIndex — writerIndex:可读部分
当读取了M个字节后:
调用discardReadBytes,会释放掉discardReadBytes的空间,并把readableBytes复制到从0开始的位置,因此这里会发生内存复制,频繁调用会影响性能
三、ByteBuf分析
ByteBuf最重要的两个基类,AbstractByteBuf提供了骨干实现,AbstractReferenceCountedByteBuf是AbstractByteBuf的子类,它的子类很常使用
1. 分类
1.1 从内存分配角度分类:
(1)堆内存字节缓冲区(HeapByteBuf):UnPoolHeapByteBuf、PooledHeapByteBuf
它的特点是内存的分配和回收都在堆,所以速度很快;缺点就是进行Socket的IO读写,需要把堆内存对应的缓冲区复制到内核Channel中,这内存复制会影响性能
(2)直接内存缓冲区(DirectByteBuf):UnPoolDirectByteBuf、UnPoolUnsafeDirectByteBuf、PoolDirectByteBuf、PoolUnsafeDirectByteBuf
它的特点是由于内存的分配在非堆(方法区),不需要内存复制,所以IO读取的速度较快,但是内存的分配较慢
总结:
根据两种内存的特点,我们可以知道,IO读写时最好使用DirectByteBuf,而在后端业务消息的解编码最好使用HeapByteBuf
1.2 从内存回收的角度分类
(1)基于对象池的ByteBuf(PoolByteBuf):PooledByteBuf和它的子类PoolDirectByteBuf、PoolUnsafeDirectByteBuf、PooledHeapByteBuf
它的特点是可以循环利用创建的ByteBuf,提高了内存的使用效率,PoolByteBuf的实现牵涉的数据结构很多,PoolByteBuf首先会申请一大块内存区域PoolArena,PoolArena由多个Chunk组成,而每个Chunk由一个或多个page组成
具体看《Netty权威指南》;
也可以看:
深入浅出Netty内存管理 PoolChunk
深入浅出Netty内存管理 PoolSubpage
深入浅出Netty内存管理 PoolChunkList
深入浅出Netty内存管理 PoolArena
(2)普通的ByteBuf(UnPoolByteBuf):UnPoolDirectByteBuf、UnPoolUnsafeDirectByteBuf、UnPoolHeapByteBuf
总结:
在高负载,大并发的情况下对象池的ByteBuf更好,而在一般情况下,可以使用UnPoolByteBuf
2. Netty的零拷贝
Netty的零拷贝主要体现在三个方面:
第一种实现:DirectByteBuf
就如上所说,ByteBuf可以分为HeapByteBuf和DirectByteBuf,当使用DirectByteBuf可以实现零拷贝
第二种实现:CompositeByteBuf
CompositeByteBuf将多个ByteBuf封装成一个ByteBuf,对外提供封装后的ByteBuf接口
第三种实现:DefaultFileRegion
DefaultFileRegion是Netty的文件传输类,它通过transferTo方法将文件直接发送到目标Channel,而不需要循环拷贝的方式,提升了传输性能
3. Netty的内存回收管理
Netty会通过 引用计数法 及时申请释放不再被引用的对象 ,实现上是通过
AbstractReferenceCountedByteBuf来实现的,我们看上面的结构图,可以看到AbstractReferenceCountedByteBuf是AbstractByteBuf的直接子类,所有具体的实现ByteBuf(堆,非堆等)都是继承自AbstractReferenceCountedByteBuf,也就是说,Netty的具体的实现ByteBuf,都是具有内存回收管理的功能的
AbstractReferenceCountedByteBuf有两个重要的成员变量:
1、AtomicIntegerFieldUpdater< AbstractReferenceCountedByteBuf> refCntUpdater
用来更新引用数,使用原子类,达到线程安全
2、volatile int refCnt = 1
用来记录引用数,保证可见性
引用+1
public ByteBuf retain() {
int refCnt;
do {
refCnt = this.refCnt;
if(refCnt == 0) {
throw new IllegalReferenceCountException(0, 1);
}
if(refCnt == 2147483647) {
throw new IllegalReferenceCountException(2147483647, 1);
}
} while(!refCntUpdater.compareAndSet(this, refCnt, refCnt + 1));
return this;
}
public ByteBuf retain() {
int refCnt;
do {
refCnt = this.refCnt;
if(refCnt == 0) {
throw new IllegalReferenceCountException(0, 1);
}
if(refCnt == 2147483647) {
throw new IllegalReferenceCountException(2147483647, 1);
}
} while(!refCntUpdater.compareAndSet(this, refCnt, refCnt + 1));
return this;
}
引用-1
public final boolean release() {
int refCnt;
do {
refCnt = this.refCnt;
if(refCnt == 0) {
throw new IllegalReferenceCountException(0, -1);
}
} while(!refCntUpdater.compareAndSet(this, refCnt, refCnt - 1));
if(refCnt == 1) {
this.deallocate();
return true;
} else {
return false;
}
}
public final boolean release() {
int refCnt;
do {
refCnt = this.refCnt;
if(refCnt == 0) {
throw new IllegalReferenceCountException(0, -1);
}
} while(!refCntUpdater.compareAndSet(this, refCnt, refCnt - 1));
if(refCnt == 1) {
this.deallocate();
return true;
} else {
return false;
}
}
可以看出,无论是增加引用还是释放引用,都是使用了CAS
refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)
1
对象时this,自身,也就是说,统计的是自身的引用数,例如对于UnPoolHeapByteBuf来说,它具有统计有多少对象引用着它,当引用数refCnt == 1时,表示此事已经没有对象引用它了,此时便调用deallocate来释放内存
---------------------
作者:JeffCoding
来源:CSDN
原文:https://blog.csdn.net/jeffleo/article/details/69230112
版权声明:本文为博主原创文章,转载请附上博文链接!
Netty ByteBuf和Nio ByteBuffer的更多相关文章
- netty byteBuf (二)
netty重新定义了byteBuf 而没使用jdk byteBuffer netty byteBuf与jdk byteBuffer的区别 (1)jdk buffer长度固定 byteBuf超过最大 ...
- Netty ByteBuf源码分析
Netty的ByteBuf是JDK中ByteBuffer的升级版,提供了NIO buffer和byte数组的抽象视图. ByteBuf的主要类集成关系: (图片来自Netty权威指南,图中有一个画错的 ...
- 对于 Netty ByteBuf 的零拷贝(Zero Copy) 的理解
此文章已同步发布在我的 segmentfault 专栏. 根据 Wiki 对 Zero-copy 的定义: "Zero-copy" describes computer opera ...
- Netty实践与NIO原理
一.阻塞IO与非阻塞IO Linux网络IO模型(5种) (1)阻塞IO模型 所有文件操作都是阻塞的,以套接字接口为例,在进程空间中调用recvfrom,系统调用直到数据包到达且被复制到应用进程缓冲区 ...
- 一步一图带你深入剖析 JDK NIO ByteBuffer 在不同字节序下的设计与实现
让我们来到微观世界重新认识 Netty 在前面 Netty 源码解析系列 <聊聊 Netty 那些事儿>中,笔者带领大家从宏观世界详细剖析了 Netty 的整个运转流程.从一个网络数据包在 ...
- 【Netty】netty学习之nio了解
[一]五种IO模型: (1)阻塞IO(2)非阻塞IO(任务提交,工作线程处理,委托线程等待工作线程处理结果的同时,也可以做其他的事情)(3)IO复用模型.(委托线程接收多个任务,将任务提交给工作线程. ...
- Netty ByteBuf(图解之 2)| 秒懂
目录 Netty ByteBuf(图解二):API 图解 源码工程 写在前面 ByteBuf 的四个逻辑部分 ByteBuf 的三个指针 ByteBuf 的三组方法 ByteBuf 的引用计数 Byt ...
- 客户端(springmvc)调用netty构建的nio服务端,获得响应后返回页面(同步响应)
后面考虑通过netty做一个真正意义的简约版RPC框架,今天先尝试通过正常调用逻辑调用netty构建的nio服务端并同步获得返回信息.为后面做铺垫 服务端实现 我们先完成服务端的逻辑,逻辑很简单,把客 ...
- 学习 java netty (一) -- java nio
前言:近期在研究java netty这个网络框架,第一篇先介绍java的nio. java nio在jdk1.4引入,事实上也算比較早的了.主要引入非堵塞io和io多路复用.内部基于reactor模式 ...
随机推荐
- UVA1618-Weak Key(RMQ)
Problem UVA1618-Weak Key Accept: 103 Submit: 588Time Limit: 3000 mSec Problem Description Cheolsoo ...
- 爬取伯乐在线文章(四)将爬取结果保存到MySQL
Item Pipeline 当Item在Spider中被收集之后,它将会被传递到Item Pipeline,这些Item Pipeline组件按定义的顺序处理Item. 每个Item Pipeline ...
- 【vue】vue +element 实现批量删除
相关资料:http://blog.csdn.net/eagle_88/article/details/70238836 作者:smile.轉角 QQ:493177502
- 3、原生jdbc链接数据库之锁与事务
一.锁的概念1.作用:是保证数据的一致性,只能一个人修改数据,不能同时多用户修改2.分类:行级锁和表级锁 乐观锁和悲观锁 二.事务1.为了保证数据的一致性和完整性,让数据库的多项操作合并为一个整体 ...
- 使用BBED跳过归档进行恢复
https: 使用BBED跳过归档进行恢复 数据库启动异常,提示6号文件丢失 SQL> startup ORACLE instance started. Total System Global ...
- CF1129E Legendary Tree 构造
传送门 神树可还行 我们令\(1\)为树根,那么如果要询问\(x\)是否在\(y\)子树中,就令\(S = \{1\} , T = \{x\} , u = y\),询问一下就可以知道了. 那么考虑先构 ...
- 史上最全面的Spring Boot Cache使用与整合
一:Spring缓存抽象 Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口 ...
- 【重磅】Spring Boot 2.1.0 权威发布
如果这两天登录 https://start.spring.io/ 就会发现,Spring Boot 默认版本已经升到了 2.1.0.这是因为 Spring Boot 刚刚发布了 2.1.0 版本,我们 ...
- WPF仿网易云音乐系列(三、播放进度条+控制按钮)
一.简介 上一篇,咱们基本把左侧导航栏给搞定,这一篇文章,开始来做一下播放进度条和控制按钮:老规矩,咱们先来看一下原版的效果: 首先,它这个专辑图片,有一个按钮效果,鼠标移入会显示出伸缩箭头:移出后消 ...
- Unity3D中Isometric Tilemap功能实践
前言 最近出于兴趣想自己做一个2D的游戏,因为有着C#的基础,所以决定使用Unity3D来做. 之前对于Unity3D其实了解不多,不过看了一些Unity3D的视频和官方文档后,暂时做起来也没遇到什么 ...