NIO —— Buffer源码分析

Buffer的类结构

底层的基础类是抽象类-Buffer,其中定义了四个变量:capacity(容量),limit(限制),position(位置),mark(标记)

在继承了Buffer后,分别对应Java的基本类型(除了Boolean)产生了7个抽象子类,并且在子类中定义了三个变量:hb(缓存数组),offset,isReadOnly

具体实现类有许多分为三大块:ByteBufferAsXXXX,DirectXXXX(缓冲区在普通内存上),HeapXXXX(缓冲区在JVM的堆上)

那么,要使用Buffer的时候如何进行实例化呢?

需要调用相应类型的Buffer中的静态方法wrap,得到一个实例对象(默认是HeapXXXX)。

在使用wrap进行构造缓冲区的时候,传入的数组在实际构造的时候只是引用了这个数组,因此在缓冲区之外对传入的数组进行操作时,均会影响到缓冲区。

Buffer中的变量描述

NIO的buffer和普通I/O的buffer中的缓存区别在于:NIO的Buffer给缓存数组定义了一系列的操作函数,方便快捷。

capacity(容量):缓存数组的大小,数组是一个final类型的变量,因此容量初始化后便不能更改,如果需要扩容,则需要新建一个buffer对象,将内容put到新对象里面。

limit(限制):第一个不能读取或者写入的索引位置。(可以使用limit()或limit(x)进行获取或修改);默认值为capacity。

position(位置):下一个要写入或者读取的索引位置。(可以使用position()或position(x)进行获取或修改);默认值为0。

mark(标记):在缓冲区中标记一个位置,在reset()时,将position的值改为mark。(可以使用mark()设置当前的position为mark值);默认值为-1;当position或者limit的值小于mark值时,此时mark作废变为-1。

几个变量直接的关系:

正常情况下,四个变量的值均不能为负数。

四个变量的大小顺序:0 <= mark <= position <= limit <= capacity

如果修改的limit值小于当前的position时,position也会相应的改变为limit值。

如果修改的position值大于当前的limit时,会直接抛出异常。

Buffer中提供的操作方法

普通方法:

remaining() —— 获取剩余空间大小(limit-position),可以作为读写时,循环的次数。

isReadOnly() —— 当前缓冲区是否是只读的

isDirect() —— 判断当前缓冲区是否是直接缓冲区;什么是直接缓冲区?正常情况下,buffer的缓冲数组是存放在JVM虚拟机中,这个数组再映射到硬盘中的地址,这会导致吞吐量低,运行效率低,因此,在初始化时可以使用静态方法allocateDirect直接分配直接地址。

put/get方法

get() —— 获取下一个值

get(int index) —— 获取第index位置的值(先判断变量是否合法,并且position值不变)

get(T[] dst, int offset, int length) —— 将length个值保存到从offset索引值开始的dst数组中(先判断变量是否合法)

在验证边界值时:如果是访问dst越界了,会抛出IndexOutOfBoundsException;如果是访问buffer越界了,会抛出BufferUnderflowException。

put(int x) —— 放入一个x值

put(int i, int x) —— 在第i位放入值x(先判断变量是否合法,并且position值不变)

put(T[] dst, int offset, int length) —— 将dst数组从offset开始的length个值存入到缓冲区中(先判断变量是否合法)

put(TBuffer src) —— 将src的缓冲区的remain内容放到当前缓冲区中(先判断变量是否合法)

***所有对数组进行的操作均是使用了System.arraycopy函数。

在验证边界值时:如果是访问src越界了,会抛出IndexOutOfBoundsException;如果是访问buffer越界了,会抛出BufferOverflowException。

因此,在get/put方法中有对数组操作时,一定要用remaining和hasRemaining函数判断剩余空间,在进行分批操作。

putType/getType

将传入的值,按照相应的type类型存储到缓冲区中。

静态方法:

allocate(int capacity) —— 生成一个固定大小的缓冲区(JVM的堆)

allocateDirect(int capacity) —— 生成一个固定大小的直接缓冲区(直接物理内存),虽然在操作的效率上高效,但是缓冲区的创建和销毁会增加消耗。创建是用Unsafe类进行创建,销毁时用虚引用引导GC对内存进行销毁。但不会立马释放

直接缓冲区和非直接缓冲区的比较:

直接缓冲区的put/get操作的效率要高于非直接缓冲区,因为在直接缓冲区中,put/get操作是直接本地系统的操作进行的,效率比通过jvm要高。

final方法:

clear() —— 将缓冲区的状态变为初始状态(limit = capacity;position = 0;mark = -1),并没有将原先的数据引用清除,准备好新一次的写入。

flip() —— 对缓存区进行反转,这个反转是将buffer从写的状态改变为读的状态(limit = position;position = 0;mark = -1)。

hasArray() —— 判断缓冲区底层是否有可用的数组。hb(数组)为null或者是isReadOnly的均返回false。

hasRemaining() —— 判断当前位置(position)和限制位置(limit)之间是否有元素。可以作为读写时,循环的次数。

rewind() —— 修改缓冲区的状态(position = 0;mark = -1),常用于重新读取缓冲区中的内容。

arrayOffset() —— 返回缓冲区中的offset变量,在写入时,起始值的偏移量

对缓冲区的操作

slice(截取) —— 按照缓冲区现在的状态,截取剩余的缓冲区,并创建一个buffer实例,但是底层的数组是共享的,也就是一边修改另一边也会有影响。

duplicate(复制) —— 复制一个状态与现在的一致的buffer实例,但是底层的数组还是共享的。

compact(压缩) —— 将剩余缓冲区压至缓冲区的最前面。

Byte缓冲区类型转换

利用asTypeBuffer可以将ByteBuffer缓冲区转换成相应类型的缓冲区,可视为一种视图(只允许读取,不允许修改)

缓冲区的比较

使用函数equal和compareTo,比较的先后顺序:

  1. 是否是自身
  2. 类型是否一致
  3. remaining是否一致
  4. position到limit之间的数据是否一致(之前不比较)

本人根据《NIO与Socket编程技术指南》和源码一起进行分析的,如有不正确的地方请进行指正,谢谢!

NIO - Buffer的更多相关文章

  1. Java NIO Buffer缓冲区

    原文链接:http://tutorials.jenkov.com/java-nio/buffers.html Java NIO Buffers用于和NIO Channel交互.正如你已经知道的,我们从 ...

  2. java NIO Buffer 详解(1)

    1.java.io  最为核心的概念是流(stream),面向流的编程,要么输入流要么输出流,二者不可兼具: 2.java.nio 中拥有3个核心概念: Selector Channel, Buffe ...

  3. Java NIO —— Buffer(缓冲区)

    Buffer是一个抽象类,位于java.nio包中,主要用作缓冲区.注意:Buffer是非线程安全类. 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO Buffer ...

  4. Java NIO Buffer(netty源码死磕1.2)

    [基础篇]netty源码死磕1.2:  NIO Buffer 1. Java NIO Buffer Buffer是一个抽象类,位于java.nio包中,主要用作缓冲区.Buffer缓冲区本质上是一块可 ...

  5. NIO Buffer 内部机理使用姿势

    关于NIO Buffer中4个重要状态属性 position.limit.capacity 与 mark Buffer本身是一个容器,称作缓冲区,里面包装了特定的一种原生类型,其子类包括ByteBuf ...

  6. Java NIO Buffer中各种状态属性的含义

    关于NIO Buffer中的3个重要状态属性的含义: postion, limit与capacity. public class NioTest { public static void main(S ...

  7. (二:NIO系列) Java NIO Buffer

    出处:Java NIO Buffer Buffer是一个抽象类,位于java.nio包中,主要用作缓冲区.Buffer缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO ...

  8. java.nio.Buffer 中的 flip()方法

    在Java NIO编程中,对缓冲区操作常常需要使用  java.nio.Buffer中的 flip()方法. Buffer 中的 flip() 方法涉及到 Buffer 中的capacity.posi ...

  9. [翻译] java NIO Buffer

    原文地址:http://tutorials.jenkov.com/java-nio/buffers.html JAVA NIO 是在和channel交互的时候使用的.正如你所知道的,数据是从chann ...

随机推荐

  1. iOS AOP框架Aspects实现原理

    总结: Aspects 是对 类的继承结构isa.mataclass结构的调整和维护:相当于链表的节点插入和删除: 同时使用method Swizzling 对方法统一重定向: 同时使用类似代理的机制 ...

  2. 在Ubuntu Desktop中安装软件

    1. 安装好虚拟机后,可以先打开firefox,看是否可以访问外部的网页. 2. 如果在公司内网,可能访问不起,需要添加代理,确保可以访问外部的网页. 3. 更新软件源 sudo apt update ...

  3. Asp.net MVC 控制器扩展方法实现jsonp

    项目需要,在使用KendoUI,又涉及到jsonp数据格式的处理,网上看到这样一种实现方法,在此小记一下(其实是因为公司里只能上博客园等少数网站,怕自己忘了,好查看一下,哈哈哈) 1. 新建控制器扩展 ...

  4. mysql报错:/usr/sbin/mysqld:unknown variable 'default-character-set=utf8'

    修改mysql的字符集时,为了将默认字符集设置为utf-8,便将/etc/my.cnf下[mysqld]加了一行 default-character-set=utf8, 但是在下次启动系统的时候,my ...

  5. kendo ui - DatePicker 日期时间系列

    kendo-ui 官网:https://www.telerik.com/documentation 初始化 grid: 引入文件: <link rel="stylesheet" ...

  6. Python 把数据库的数据导出到excel表

    import io,xlwt def export_excel(request): """导出数据到excel表""" list_obj = ...

  7. 基于Naive Bayes算法的文本分类

    理论 什么是朴素贝叶斯算法? 朴素贝叶斯分类器是一种基于贝叶斯定理的弱分类器,所有朴素贝叶斯分类器都假定样本每个特征与其他特征都不相关.举个例子,如果一种水果其具有红,圆,直径大概3英寸等特征,该水果 ...

  8. webStorm常用设置之过滤文件夹

    webStorm是一款很好用的前端IDE,但是不能否认的是webStorm很重,没有sublime轻便 尤其是项目cnpm后,加载node_modules时的状态,简直想那块豆腐自杀有莫有,就算你耐心 ...

  9. L2-014. 列车调度

    L2-014. 列车调度 时间限制 300 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 火车站的列车调度铁轨的结构如下图所示. Figure ...

  10. cloudstack-kvm-libvirtd

    2.4.libvirtd日志和VM的日志 在运行libvirtd的时候,我们需要获得lbivirtd的运行信息.所以我们需要找到他的日志文件.一般情况下,它是在/var/log/libvirt/lib ...