Java NIO(二)缓冲区
概念
缓冲区:一个用于特定基本数据类型的容器,由java.nio包定义的所有缓冲区都是Buffer抽象类的子类。其作用于与NIO的通道进行交互,数据从通道读入缓冲区,数据从缓冲区写入通道
Buffer的基本用法
使用Buffer读写数据一般遵循以下四个步骤:
- 写入数据到Buffer
- 调用flip()方法
- 从Buffer中读取数据
- 调用clear()方法或compact()方法清除缓冲区中的数据
当向Buffer中写入数据时,Buffer会记录写下了多少数据,一旦要读取数据,通过flip()方法将Buffer从写模式切换到读模式。在读模式下,通道可以读取之前写入到Buffer的所有数据
一旦读取完所有数据,就需要调用clear()或compact()清空缓冲区,让它可以再次被写入。clear()方法会清空缓冲区里的所有数据,compact()方法只会清除已经读取过的数据,任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面
缓冲区的本质是一块可以写数据,可以从中读取数据的内存
Buffer常用子类:
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffe
上述类都采用类似的方法管理数据,都是通过下面的方法获取Buffer对象:
static XxxBuffer allocate(int capacity):创建一个容量为capacity的对象
补充: 这些缓冲区都为抽象类,不能被实例化,所以通过allocate()方法来实例化。实例化的对象为HeapXxxBuffer,默认大小为100,如HeapCharBuffer等
Buffer的基本属性
容量(capacity): 表示Buffer的最大数据容量,不能为负,且创建后不能修改
限制(limit): 第一个不应该读取或写入的数据的索引,即位于limit之后的数据都不能读写,该值不能为负且不能大于capacity。在写模式下,该值等于capacity,在读模式下,该值会被设置成读模式下的position
位置(position): 下一个要读取或写入的数据的索引,其值不能为负且不能大于limit,其初始值为0,最大值可为capacity-1。当Buffer从写模式切换到读模式时,position置为0
标记(mark)与重置(reset): 标记是一个索引,通过mark()方法指定Buffer中一个特定的position,之后调用reset()方法恢复到这个position
补充:标记,位置,限制,容量遵循以下不变式: 0<=mark<=position<=limit<=capacity
向Buffer中写数据
写数据到Buffer中有两种方式:
- 从Channel写到Buffer
- 通过Buffer的put()方法
int bytes = channel.read(buf);
或
buf.put(127);
注意: put方法有很多版本,允许以不同的方式把数据写入到Buffer中。例如, 写到一个指定的位置,或者把一个字节数组写入到Buffer(批量写入)
从Buffer中读数据
从Buffer中读数据的两种方式:
- 从Buffer中读取数据到Channel
- 通过Buffer的get()方法
int bytes = channel.write(buf);
或
buf.get();
注意: get方法有很多版本,允许你以不同的方式从Buffer中读取数据。例如,从指定position读取,或者从Buffer中读取数据到字节数组(批量读取数据)
flip()方法
flip()方法将Buffer从写模式切换到读模式。将limit设置为position的值,再将position置为0
rewind()方法
rewind()方法将position置为0,limit保持不变。你能够重读Buffer中的所有数据
clear()与compact()方法
调用clear()方法,position置为0,limit设为capacity的值,Buffer被清空了,但Buffer的数据未被清除,只是这些标记告诉我们从哪个位置将数据写入到Buffer中
如果Buffer中有一些未读的数据,调用clear()方法,数据将“被遗忘”,意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有
compact()方法将所有未读的数据复制到Buffer的起始处,然后将position设置为未读的数据的最后一个的后面,limit设置为capacity的值
mark()和reset()方法
通过mark()方法指定Buffer中一个特定的position,之后调用reset()方法恢复到这个position
equals()方法
当满足以下条件时,两个Buffer相等:
- 有相同的类型(如byte,char等)
- Buffer中剩余的byte,char等的个数相等
- Buffer中所有剩余的byte,char等都相等
equals只是比较Buffer的一部分,而不是全部,实际上它只比较Buffer的剩余元素
compareTo()方法
compareTo()方法必将两个Buffer的剩余元素,如果满足下列条件,则认为一个Buffer小于另一个Buffer:
- 第一个不相等的元素小于另一个Buffer中对应的元素
- 所有元素都相等,但第一个Buffer比另外一个先耗尽(第一个Buffer的元素比另外一个少)
注意: 剩余元素是position到limit之间的元素
字节缓冲区
字节缓冲区和其它缓冲区最明显的不同在于它们可能成为通道所执行I/O的源头或目标,通道只接收ByteBuffer作为参数
操作系统在内存区域进行I/O操作,这些内存区域就操作系统方面而言是相连的字节序列,于是,只有字节缓冲区有资格参与I/O操作。而在JVM中,字节数组可能不会在内存中连续存储或者无用存储单元收集可能随时对其进行移动。且在JVM中,数组是对象,数据存储在对象中的方式在不同的JVM中实现也不同
所以,引出了直接缓冲区的概念
直接缓冲区
API中直接缓冲区的介绍:
- 字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O 操作。也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
- 直接字节缓冲区可以通过调用此类的 allocateDirect()工厂方法来创建。此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的本机 I/O 操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。
- 直接字节缓冲区还可以通过FileChannel的map()方法将文件区域直接映射到内存中来创建。Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区。如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在访问期间或稍后的某个时间导致抛出不确定的异常。
- 字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其 isDirect方法来确定。提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。
直接字节缓冲区通常是I/O操作最好的选择。在设计方面,它们支持JVM可用的最高效I/O机制,非直接字节缓冲区可以被传递给通道,但是这样可能导致性能损耗,通常非直接缓冲不可能成为一个本地I/O操作的目标,如果开发者向一个通道中传递一个非直接ByteBuffer对象用于写入,通道可能会在每次调用中隐含地进行下面的操作:
- 创建一个临时的直接ByteBuffer对象
- 将非直接缓冲区的内容复制到直接缓冲区中
- 使用临时缓冲区执行低层次的I/O操作
- 临时缓冲区对象离开作用域,被回收
这可能导致缓冲区在每个I/O上复制并产生大量的对象,这是应该极力避免的
直接缓冲区是I/O的最佳选择,但可能比创建非直接缓冲区要花费更高的成本。直接缓冲区使用的内存是通过调用本地操作系统的代码分配的,绕过了标准JVM堆栈。直接缓冲区的内存区域不受无用存储单元收集支配,因为它们位于标准JVM堆栈之外。
那么是否在创建缓冲区时,都应该创建直接缓冲区呢?
不一定,因为滥用allocateDirect()方法创建直接字节缓冲区是有风险的
使用allocateDirect方法在堆外分配内存,这块内存区域的回收依赖Full GC,且回收效率不高,这样将导致内存越来越大,甚至导致内存溢出。普通的创建字节缓冲区的方法,内存分配在年轻代,易于回收
Java NIO(二)缓冲区的更多相关文章
- Java NIO——2 缓冲区
一.缓冲区基础 1.缓冲区并不是多线程安全的. 2.属性(容量.上界.位置.标记) capacity limit 第一个不能被读或写的元素 position 下一个要被读或写的元素索引 mark ...
- Java NIO ———— Buffer 缓冲区详解 入门
引言缓冲区是一个用于特定基本类型的容器.由java.nio 包定义,所有缓冲区都是 Buffer 抽象类的子类. Java NIO 中的 Buffer ,主要用于与NIO 通道进行交互.数据从通道存入 ...
- Java NIO 之缓冲区
缓冲区基础 所有的缓冲区都具有四个属性来 供关于其所包含的数据元素的信息. capacity(容量):缓冲区能够容纳数据的最大值,创建缓冲区后不能改变. limit(上界):缓冲区的第一个不能被读或写 ...
- Java NIO之缓冲区Buffer
Java NIO的核心部件: Buffer Channel Selector Buffer 是一个数组,但具有内部状态.如下4个索引: capacity:总容量 position:下一个要读取/写入的 ...
- Java NIO Buffer缓冲区
原文链接:http://tutorials.jenkov.com/java-nio/buffers.html Java NIO Buffers用于和NIO Channel交互.正如你已经知道的,我们从 ...
- Java NIO之缓冲区
1.简介 Java NIO 相关类在 JDK 1.4 中被引入,用于提高 I/O 的效率.Java NIO 包含了很多东西,但核心的东西不外乎 Buffer.Channel 和 Selector.这其 ...
- java NIO (二) 一个故事讲清楚NIO
假设某银行只有10个职员.该银行的业务流程分为以下4个步骤: 1) 顾客填申请表(5分钟): 2) 职员审核(1分钟): 3) 职员叫保安去金库取钱(3分钟): 4) 职员打印票据,并将钱和票据返回给 ...
- Java NIO -- 直接缓冲区与非直接缓冲区
直接缓冲区与非直接缓冲区: 非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建 ...
- Java NIO流 -- 缓冲区(Buffer,ByteBuffer)
用来定义缓冲区的所有类都以Buffer类为基类,Buffer定义了缓冲区的基本特征. 直接子类: ByteBuffer 用来存储byte类型的缓冲区,可以在这种缓冲区中存储任意其他基本类型的二进制值( ...
- 海纳百川而来的一篇相当全面的Java NIO教程
目录 零.NIO包 一.Java NIO Channel通道 Channel的实现(Channel Implementations) Channel的基础示例(Basic Channel Exampl ...
随机推荐
- 全栈框架——MEAN
MEAN: MongoDB - Express - AngularJs - Node.js MongoDB 是一个面向文档的. NoSQL 类型的数据库.MongoDB 颠覆了传统的基于表的数据存储方 ...
- js prototype 原型
//https://xxxgitone.github.io/2017/06/10/JavaScript%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%E7%9A%84%E4% ...
- JavaScript数组操作函数
A: 购物车会有这样的情况,购物车是一个数组,每一个商品是一个对象,分别对应一个id,和一个num ,然后改变商品的时候需要和购物车对比,如果购物车中有这个商品的话,就只改变这个商品对应的id的num ...
- jquery获取元素内容-text()和val()
不传参数的text()方法在获取文本内容时,会把子元素的文本也获取过来(会删除 HTML 标记),例子: <!doctype html> <html> <head> ...
- 序列终结者 Splay
1.注意在 split 和 merge时要特判一下边界, 否则就会出现边界错误的情况. 2.随时都要维护父指针. 3.在更新 maxv 和翻转标记时要判一下左右儿子是否都存在. #include< ...
- Vue2实例中的data属性三种写法与作用
<script src="https://unpkg.com/vue/dist/vue.js"></script> <div id="app ...
- elasticsearch批量操作
1.批量查询的好处 就是一条一条的查询,比如说要查询100条数据,那么就要发送100次网络请求,这个开销还是很大的 如果进行批量查询的话,查询100条数据,就只要发送1次网络请求,网络请求的性能开销缩 ...
- pip是用代理
内网访问外网时,需要通过vpn访问,但是pip貌似不能是用vpn访问外网.直接度娘: windows下: set http_proxy=http://代理服务器:端口 不行可以试试下面的: set h ...
- 完全二叉树(Complete Binary Tree)
Date:2019-03-25 19:36:45 判断一棵树是否为完全二叉树 #include <queue> using namespace std; void IsComplete(n ...
- selenium的显示等待和隐式等待区别
1.selenium的显示等待 原理:显式等待,就是明确的要等到某个元素的出现或者是某个元素的可点击等条件,等不到,就一直等,除非在规定的时间之内都没找到,那么就跳出Exception.(简而言之:就 ...