概念

缓冲区:一个用于特定基本数据类型的容器,由java.nio包定义的所有缓冲区都是Buffer抽象类的子类。其作用于与NIO的通道进行交互,数据从通道读入缓冲区,数据从缓冲区写入通道

Buffer的基本用法

使用Buffer读写数据一般遵循以下四个步骤:

  1. 写入数据到Buffer
  2. 调用flip()方法
  3. 从Buffer中读取数据
  4. 调用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(二)缓冲区的更多相关文章

  1. Java NIO——2 缓冲区

    一.缓冲区基础 1.缓冲区并不是多线程安全的. 2.属性(容量.上界.位置.标记) capacity limit  第一个不能被读或写的元素 position  下一个要被读或写的元素索引 mark ...

  2. Java NIO ———— Buffer 缓冲区详解 入门

    引言缓冲区是一个用于特定基本类型的容器.由java.nio 包定义,所有缓冲区都是 Buffer 抽象类的子类. Java NIO 中的 Buffer ,主要用于与NIO 通道进行交互.数据从通道存入 ...

  3. Java NIO 之缓冲区

    缓冲区基础 所有的缓冲区都具有四个属性来 供关于其所包含的数据元素的信息. capacity(容量):缓冲区能够容纳数据的最大值,创建缓冲区后不能改变. limit(上界):缓冲区的第一个不能被读或写 ...

  4. Java NIO之缓冲区Buffer

    Java NIO的核心部件: Buffer Channel Selector Buffer 是一个数组,但具有内部状态.如下4个索引: capacity:总容量 position:下一个要读取/写入的 ...

  5. Java NIO Buffer缓冲区

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

  6. Java NIO之缓冲区

    1.简介 Java NIO 相关类在 JDK 1.4 中被引入,用于提高 I/O 的效率.Java NIO 包含了很多东西,但核心的东西不外乎 Buffer.Channel 和 Selector.这其 ...

  7. java NIO (二) 一个故事讲清楚NIO

    假设某银行只有10个职员.该银行的业务流程分为以下4个步骤: 1) 顾客填申请表(5分钟): 2) 职员审核(1分钟): 3) 职员叫保安去金库取钱(3分钟): 4) 职员打印票据,并将钱和票据返回给 ...

  8. Java NIO -- 直接缓冲区与非直接缓冲区

    直接缓冲区与非直接缓冲区: 非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建 ...

  9. Java NIO流 -- 缓冲区(Buffer,ByteBuffer)

    用来定义缓冲区的所有类都以Buffer类为基类,Buffer定义了缓冲区的基本特征. 直接子类: ByteBuffer 用来存储byte类型的缓冲区,可以在这种缓冲区中存储任意其他基本类型的二进制值( ...

  10. 海纳百川而来的一篇相当全面的Java NIO教程

    目录 零.NIO包 一.Java NIO Channel通道 Channel的实现(Channel Implementations) Channel的基础示例(Basic Channel Exampl ...

随机推荐

  1. 5 Python+Selenium的元素定位方法(xpath)

    [环境] Python3.6+selenium3.0.2+FireFox50+win7 [定位方法] 1.方法:find_element_by_xpath('') 说明:xpath定位方法有相对路径和 ...

  2. ES : 软件工程学的复杂度理论及物理学解释

    系统论里面总是有一些通用的专业术语 比如复杂度.熵.焓,复杂度专门独立出来,成为复杂度理论 文章摘抄于:<非线性动力学> 刘秉政 编著  5.5 复杂性及其测度 热力学的几个专业术语 熵. ...

  3. Jquery数字转盘:

    项目中,在充值流程中,加入了1个抽奖环节,需要转盘显示抽中的虚拟货币.网上找了相关的特效,最后锁定在这个特效上:http://www.jb51.net/jiaoben/319636.html.因为用的 ...

  4. Here comes Treble: A modular base for Android

    On the Android team, we view each dessert release as an opportunity to make Android better for our u ...

  5. CreateFile打开文件或者打开目录

    一.打开目录 参数列表: lpFileName String 要打开的文件的名字 dwDesiredAccess Long 如果为 GENERIC_READ 表示允许对设备进行读访问:如果为 GENE ...

  6. Oracle语句执行顺序

  7. Java IO 流总结

    Java流操作有关的类或接口: Java流类图结构: 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

  8. css 垂直居中方法总结

    工作中遇到垂直居中问题,特此总结了一下几种方式与大家分享.本文讨论的垂直居中仅支持IE8+ 1.使用绝对定位垂直居中 HTML <div class="container"& ...

  9. jsp js action之间传值

    1.struts2 action如何向JSP的JS函数传值 action中定义变量 public class TestAction extends ActionSupport implements S ...

  10. 【hihocoder 1304】搜索一·24点

    [题目链接]:http://hihocoder.com/problemset/problem/1304 [题意] [题解] 按照题目给的方法搜索就好; 那个方法很棒啊. 注意除0; 然后是浮点数的比较 ...