Netty 教程 – ByteBuf详解
ByteBuffer存在的问题
ByteBuffer
是JDK1.4
中提供的java.nio.Buffer
, 在内存中预留指定大小的存储空间来存放临时数据,其他Buffer
的子类有:CharBuffer
、DoubleBuffer
、FloatBuffer
、IntBuffer
、LongBuffer
和 ShortBuffer
ByteBuffer
的长度是固定的,一旦分配完成,容量就无法动态扩容收缩,分多了会浪费内存,分少了存放大的数据时会索引越界(当传输数据大于初始化长度
时,会出现BufferOverflowException
索引越界的异常),所以使用ByteBuffer时
,为了解决这个问题,我们一般每次put
操作时,都会对可用空间进行校检,如果剩余空间不足,需要重新创建一个新的ByteBuffer
,然后将旧的ByteBuffer
复制到新的ByteBuffer
中去ByteBuffer
中只有通过position
获得当前可操作的位置,调用get()
方法,返回ByteBuffer[postion]
处的值,如果是调用put方法,将数据放入ByteBuffer[postion]
的位置API
功能有限,部分高级功能并不支持,需开发者自己实现,且使用原生ByteBuffer
较为困难(不适合小白专业户)
ByteBuf与ByteBuffer的区别
不同
ByteBuf
实现原理各不相同,我们先看最基本的ByteBuf
与原生的ByteBuffer
的区别
ByteBuf buf = Unpooled.buffer(10);
buf.writeBytes("鏖战八方QQ群391619659".getBytes());//扩容算法稍后讲解
System.out.println("Netty:" + buf);
byte[] by = new byte[buf.readableBytes()];
buf.readBytes(by);
System.out.println("Netty:" + new String(by));
System.out.println("//无耻的分割线//");ByteBuffer bf1 = ByteBuffer.allocate(100);
bf1.put("鏖战八方QQ群391619659".getBytes());
System.out.println("JDK:"+bf1);
System.out.println("当前指针:" + bf1.position());
byte[] by1 = new byte[bf1.remaining()];
System.out.println(by1.length);//What's 居然是74
bf1.get(by1);
System.out.println("未使用flip:"+new String(by1));//居然是空的
System.out.println("//无耻的分割线//");ByteBuffer bf2 = ByteBuffer.allocate(100);
bf2.put("鏖战八方QQ群391619659".getBytes());
System.out.println("JDK:"+bf2);
System.out.println("当前指针:" + bf2.position());
bf2.flip();
byte[] by2 = new byte[bf2.remaining()];
System.out.println(by2.length);//是26了
bf2.get(by2);
System.out.println("使用flip:"+new String(by2));//拿到了
指针区别
从日志输出中可以看到,使用JDK自带
的特别的麻烦,远远没有ByteBuf
来的方便,无需关心读写切换指针的问题,在JDK
中,由于只有一个一个position
指针,我们需要通过flip()
进行转换控制,而Netty
却可以很好的帮我们做到扩容,它的内部维护了readerIndex
与writerIndex
两个指针,一开始都是0
,随着数据的写入writerIndex
会增加但不会超过readerIndex
,当我们读取后内部会通过调用discardReadBytes
来释放这部分空间,类似ByteBuffer
的compact
方法,readerIndex
与writerIndex
都是可读取的,等同ByteBuffer
中position -> limit
之间的数据,WriterIndex
和capacity
之间空间是可写的,等同ByteBuffer
中limit -> capacity
前面说到过,
JDK
自带的ByteBuffer
无法做到自动扩容,当内容超出的时候会抛出索引越界的异常
,接下来看一段代码
ByteBuf buf = Unpooled.buffer(10);
buf.writeBytes("鏖战八方QQ群391619659".getBytes());//扩容算法稍后讲解
System.out.println(buf);
System.out.println("//无耻的分割线//");
ByteBuffer buffer = ByteBuffer.allocate(10);
buffer.put("鏖战八方QQ群391619659".getBytes());
System.out.println(buffer);
问题:为什么Netty
的ByteBuf
没有报错,capacity
为什么会自动扩容呢?扩容的大小是怎么计算的?
@Override
public ByteBuf writeBytes(byte[] src) {writeBytes(src, 0, src.length);return this;
}@Override
public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {ensureAccessible();ensureWritable(length);setBytes(writerIndex, src, srcIndex, length);writerIndex += length;return this;
}@Override
public ByteBuf ensureWritable(int minWritableBytes) {if (minWritableBytes < 0) {throw new IllegalArgumentException(String.format("minWritableBytes: %d (expected: >= 0)", minWritableBytes));}if (minWritableBytes <= writableBytes()) {return this;}if (minWritableBytes > maxCapacity - writerIndex) {throw new IndexOutOfBoundsException(String.format("writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",writerIndex, minWritableBytes, maxCapacity, this));}// 默认64 当前大小 <<= 1 翻倍int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);// Adjust to the new capacity.capacity(newCapacity);return this;
}
扩容分析
- 调用
writeBytes
方法后,ByteBuf
会修改writerIndex
的指针大小,然后判断最小写入字节
是否大于0
,如果都没内容这不是在玩它么(*^▽^*)
- 接下来是
最小写入字节
小于或者等于剩余容量
,那就返回当前的ByteBuf,也就不会扩容了,因为能装得下
- 接下来继续判断
最小写入字节
是否大于当前ByteBuf最大容量 - 已使用容量
,如果最大容量
都装不下说明这已经没法继续玩了,只能装这么多,扩容不了 - 最后就是
扩容处理
了,Netty
的做法是默认64字节
,小于阀值取64
,大于取64 <<= 1
,成倍递增
相比其它的JAVA对象,缓冲区的分配(包括动态扩容)与释放是一个耗时操作,因此需要尽可能的复用
@Override
public ByteBuf discardReadBytes() {ensureAccessible();if (readerIndex == 0) {return this;}if (readerIndex != writerIndex) {setBytes(0, this, readerIndex, writerIndex - readerIndex);writerIndex -= readerIndex;adjustMarkers(readerIndex);readerIndex = 0;} else {adjustMarkers(readerIndex);writerIndex = readerIndex = 0;}return this;
}
public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;
}
Netty 教程 – ByteBuf详解相关推荐
- Netty 教程 – 解码器详解
TCP以流的方式进行数据传输,上层的应用为了对消息进行区分,往往采用如下方式 固定消息长度,累计读取到长度和定长LEN的报文后,就认为读取到了个完整的消息,然后将计数器位置重置在读取下一个报文内容 将 ...
- Netty之ByteBuf详解
1. ByteBuf的创建 在Netty中,有一个比较常见的对象ByteBuf,它其实等同于Java Nio中的ByteBuffer,但是ByteBuf对Nio中的ByteBuffer的功能做了很作增 ...
- netty系列之:netty中的ByteBuf详解
文章目录 简介 ByteBuf详解 创建一个Buff 随机访问Buff 序列读写 搜索 其他衍生buffer方法 和现有JDK类型的转换 总结 简介 netty中用于进行信息承载和交流的类叫做Byte ...
- java 检查bytebuf长度_Java学习笔记16-Netty缓冲区ByteBuf详解
Java学习笔记16-Netty缓冲区ByteBuf详解 Netty自己的ByteBuf ByteBuf是为解决ByteBuffer的问题和满足网络应用程序开发人员的日常需求而设计的. JDK Byt ...
- 支付接口教程,详解支付宝接口(二)
支付宝的接口向来集成过程都让人觉得比较舒服,只有APP支付相对复杂,但也只是配置上复杂一些,只要清楚原理相信也不是什么难事.下面是以前介绍双钥加密原理的传送门: 支付接口教程特别篇,公钥与私钥,双钥加 ...
- python3.6安装教程-python3.6环境安装+pip环境配置教程图文详解
1.python安装可以跨平台 2.有两个版本2.7和3.6,第三方库适用2.7版,两个版本不兼容 windows安装: 第一种方法官网安装: 在官网下载安装包如图: 图下点击是默认下载32位所以我们 ...
- python详细安装教程环境配置-python3.6环境安装+pip环境配置教程图文详解
1.python安装可以跨平台 2.有两个版本2.7和3.6,第三方库适用2.7版,两个版本不兼容 windows安装: 第一种方法官网安装: 在官网下载安装包如图: 图下点击是默认下载32位所以我们 ...
- Redis数据库教程——系统详解学习Redis全过程
Redis数据库教程--系统详解学习Redis全过程 Redis快速入门:Key-Value存储系统简介 Key-Value存储系统: Key-Value Store是当下比较流行的话题,尤其 ...
- python3.6.0怎么安装pip_python3.6环境安装+pip环境配置教程图文详解
1.python安装可以跨平台 2.有两个版本2.7和3.6,第三方库适用2.7版,两个版本不兼容 windows安装: 第一种方法官网安装: 在官网下载安装包如图: 图下点击是默认下载32位所以我们 ...
最新文章
- python 判断字符串是否包含另一个字符串_强烈推荐:Python字符串(string)方法整理(一)...
- codevs2693 上学路线(施工)
- 图解Oracle包实例
- python创建配置文件_如何写python的配置文件
- nacos 适配达梦、人大金仓数据库
- matlab中noisbloc,基于小波变换的微弱信号检测技术的研究.doc
- MatConvNet深度学习工具箱安装教程
- 【Python成长之路】python 基础篇 -- 装饰器【华为云分享】
- Windows程序设计_19_测试Windows应用程序加载函数
- 在c语言程序中整型常量不能表示的数制是,C源程序中不能表示的数制有哪些
- 自定义MyBatis拦截器
- 电脑课学生端密码查看
- 广袤之中:沿着克拉克三大定律,读懂华为的最深期待
- 传感器技术-光电式传感器(学习笔记十)
- 通过比赛整理出的8条Numpy实用技巧【你知道如何频数统计和按某列进行排序么?】...
- vue调倍速后声音变了_pr加速人声后声音变调失真怎么办?
- 用Python控制摄像头拍照并发邮件
- 机电照明工程软件测试大纲,2019最新大纲 | 公路水运工程试验检测考试大纲《交通工程》...
- [模块加载失败:找不到指定的模块]——如何解决DLL加载问题?
- SPICE的器件模型