🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 简介 **缓冲区(buffer): 存储数据的** **通道(channel): 代表数据源与目标节点之间的连接,负责缓冲区的传输** Buffer就像一个数组,可以保存多个相同类型的数据.根据数据类型不同(boolean除外),有以下Buffer常用子类 ![](https://img.kancloud.cn/56/db/56dba22ed9bcc053147560a6a739c562_692x278.png) 上面的Buffer类,他们都采用相似的方法进行管理数据,只是各自管理的数据类型不同而已. 都是通过各自类的如下方法获取一个Buffer对象 ~~~ //创建一个容量为capacity的XxxxBuffer对象 static XxxBuffer allocate(int capacity) ~~~ **缓冲区的基本属性** * 容量(capacity): 表示Buffer最大数据容量,一旦声明后,不能更改,通过Buffer中的capacity()获取.缓冲区capacity不能为负 * 限制(limit): 第一个不应该读取或写入的数据的索引,即位于limit后的数据不可读写.通过Buffer中的limit()获取.缓冲区的limit不能为负,并且不能大于其capacity * 位置(position): 当前要读取或写入数据的索引.通过Buffer中position()获取.缓冲区的position不能为负,并且不能大于其limit,下一个要读取或写入的数据的索引。 * 标记(mark): 标记是一个索引,通过Buffer中的mark()方法将mark标记为当前position位置.之后可以通过调用reset()方法将position恢复到标记的mark处 遵守以下公式 ~~~ 0 <= mark <= position <= limit <= capacity ~~~ ## 缓冲区(Buffer) 通过上面NIO与普通IO的主要区别也可以看到在基本的IO操作中所有的操作都是基于流进行操作的,而在NIO中所有的操作都是基于缓冲区继续操作的,所有的读写操作都是通过缓存区来进行完成,缓冲区(Buffer)是一个线性的、有序的数据集,只能容纳特定的数据类型(基本就是基本数据类型对应的Buffer或者起子类)。 ### 各数据类型的缓存区类 ![](https://box.kancloud.cn/d7f4d636e61f46c59390db857fd94f48_511x346.png) 备注:看到上面这几类是不是想起了JAVA的8种基本数据类型,唯一缺少boolean对于的类型。 第一问:为什么boolean不需要缓存呢? 可以查阅之前写的:java二进制相关基础,里面有描述规范中数字的内部表示和存储, boolean所占位数1bit(取值只有true或者false), 由于字节(byte)是操作系统和所有I/O设备使用的基本数据类型, 所以基本都是以字节或者连续的一段字节来存储表示, 所以就没有boolean,感觉也没有必要boolean类型的缓存操作 ### Buffer使用 **读数据:** * flip()方法 * 将Buffer从写模式切换到读模式 * **调用flip()方法会将position设回0**,并将limit设置成之前position的值。 * buf.flip(); * buf.get() * 读取数据 * Buffer.rewind() * 将position设回0,所以你可以重读Buffer中的所有数据 * limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等) * Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用。 * Buffer.reset()方法,恢复到Buffer.mark()标记时的position。 * clear()方法会: * 清空整个缓冲区。 * position将被设回0,limit被设置成 capacity的值 * compact()方法: * 只会清除已经读过的数据;任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。 * 将position设到最后一个未读元素正后面,limit被设置成 capacity的值。 **写数据**:buf.put(127); ### Buffer其他方法 ![](https://img.kancloud.cn/30/39/303965485b8061c03e7b446d53e8c071_975x624.png) ### 缓冲区的基本属性 ![](https://box.kancloud.cn/57f76684a9eec30d90b791b144c55f57_925x291.png) ![](https://img.kancloud.cn/57/4b/574ba217fd97876635fdf3cba6b6404c_927x650.png) ~~~ //第一步,获取IntBuffer,通过IntBuffer.allocate操作 IntBuffer buf = IntBuffer.allocate(10) ; // 准备出10个大小的缓冲区 //第二步未操作前输出属性值 System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ; //第三步进行设置数据 buf.put(6) ; // 设置一个数据 buf.put(16) ; // 设置二个数据 //第四步操作后输出属性值 System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ; //第五步将Buffer从写模式切换到读模式 postion = 0 ,limit = 原本position buf.flip() ; //第六步操作后输出属性值 System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ; ~~~ 程序输出结果: ~~~ position = 0,limit = 10,capacty = 10 position = 2,limit = 10,capacty = 10 position = 0,limit = 2,capacty = 10 ~~~ # 直接与非直接缓冲区 ~~~ 物理磁盘->内核地址空间->用户地址空间->应用程序 OS                              ->                           JVM 直接缓冲区 内核地址空间和用户地址空间之间形成了一个物理内存映射文件,减少了之间的copy过程。 存在风险: 用于不易控制 GC ~~~ ![](https://img.kancloud.cn/4b/ee/4beecb868dfce0fac20a3129ba0a337c_788x376.png) ![](https://img.kancloud.cn/1c/85/1c85ae285f692eea6b21137c7c74c974_776x498.png) * 字节缓冲区要么是直接的,要么是非直接的.如果为直接字节缓冲区,则java虚拟机会尽最大努力直接在此缓冲区上执行本机IO操作.也就是说,在每次调用基础操作系统的一个本机IO操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓存区(或从中间缓冲区中复制内容) * 直接字节缓冲区可以通过调用ByteBuffer的allocateDirect()工厂方法来创建.此方法返回缓冲区进行分配和取消分配所需要成本通常高于非直接缓冲区.直接缓冲区的内容可以驻留在常规垃圾回收堆之外.因此,他们对应用程序内存需求量造成的影响可能并不明显.所以,建议将直接缓冲区主要分配给哪些易受基础系统的本机IO操作影响的大型,持久的缓冲区.一般情况下,最好在直接缓冲区能在程序性能方面带来明显好处的时候使用他们 * 直接字节缓冲区还可以通过FileChannel的map()方法将文件区域直接映射到内存中创建.该方法返回ByteBuffer的子类:MappedByteBuffer.java平台的实现有助于通过JNI从本机代码创建直接字节缓冲区.如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在访问期间或稍后的某个时间导致抛出不确定的异常 * 字节缓冲区是直接缓存区还是非直接缓冲区可通过调用其isDirect()方法来确定.提供此方法是为了能够在性能关键型代码中执行显示缓冲区管理