NIO核心一:缓冲区(Buffer)
缓冲区(Buffer)
一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区 都是 Buffer 抽象类的子类.。Java NIO 中的 Buffer 主要用于与 NIO 通道进行 交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
Buffer 类及其子类
Buffer 就像一个数组,可以保存多个相同类型的数据。根 据数据类型不同 ,有以下 Buffer 常用子类:
-
ByteBuffer -
CharBuffer -
ShortBuffer -
IntBuffer -
LongBuffer -
FloatBuffer -
DoubleBuffer
上述 Buffer 类 他们都采用相似的方法进行管理数据,只是各自 管理的数据类型不同而已。都是通过如下方法获取一个 Buffer 对象:
static XxxBuffer allocate(int capacity) : 创建一个容量为capacity 的 XxxBuffer 对象
缓冲区的基本属性
Buffer 中的重要概念:
-
容量 (capacity) :作为一个内存块,Buffer具有一定的固定大小,也称为”容量”,缓冲区容量不能为负,并且创建后不能更改。
-
位置 (position) :下一个要读取或写入的数据的索引。缓冲区的位置不能为 负,并且不能大于其限制
-
限制 (limit) :表示缓冲区中可以操作数据的大小(limit 后数据不能进行读写)。缓冲区的限制不能为负,并且不能大于其容量。写入模式,限制等于buffer的容量。读取模式下,limit等于写入的数据量。
-
标记 (mark)与重置 (reset) :标记是一个索引,通过 Buffer 中的 mark() 方法 指定 Buffer 中一个特定的 position,之后可以通过调用 reset() 方法恢复到这 个 position. *标记、位置、限制、容量遵守以下不变式:0
-
*图示:
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
position 到 limit 是可用区域。
假如 position=0,limit=10,capacity=10
写的时候,**0-10是可写入区域**。
如果写了5个字节,那么**5-10是可写入区域。**
调用flip,会将position赋值给limit,position变为0。即**0-5是可读区域。**
如果读完5个自己 就是 position =5 ,limit=5,capacity=10
Buffer常见方法
Buffer clear() 清空缓冲区并返回对缓冲区的引用
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
Buffer flip() 为 将缓冲区的当前位置赋值给界限,并将当前位置充值为 0
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
int capacity() 返回 Buffer 的 capacity 大小
boolean hasRemaining() 判断缓冲区中是否还有元素
int limit() 返回 Buffer的界限(limit) 的位置
Buffer limit(int n) 将设置缓冲区界限为 n, 并返回一个具有新 limit 的缓冲区对象
Buffer mark() 对缓冲区设置位置标记
public final Buffer mark() {
mark = position;
return this;
}
int position() 返回缓冲区的当前位置 position
Buffer position(int n) 将设置缓冲区的当前位置为 n , 并返回修改后的 Buffer 对象
int remaining() 返回 position 和 limit 之间的元素个数
Buffer reset() 将位置 position 转到以前设置的 mark 所在的位置。还原位置标记。
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
Buffer rewind() 将位置position设为为 0, 取消设置的 mark
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
缓冲区的数据操作
Buffer 所有子类提供了两个用于数据操作的方法:get()put() 方法
获取Buffer中的数据
get() :读取单个字节
get(byte[] dst):批量读取多个字节到 dst 中
get(int index):读取指定索引位置的字节(不会移动 position)
将数据放到Buffer中
put(byte b):将给定单个字节写入缓冲区的当前位置
put(byte[] src):将 src 中的字节写入缓冲区的当前位置
put(int index, byte b):将指定字节写入缓冲区的索引位置(不会移动 position)
使用Buffer读写数据一般遵循以下四个步骤:
-
1.写入数据到Buffer -
2.调用flip()方法,转换为读取模式 -
3.从Buffer中读取数据 -
4.调用buffer.clear()方法或者buffer.compact()方法清除缓冲区
案例演示
public class TestBuffer {
public void test1(){
String str = "itheima";
ByteBuffer buf = ByteBuffer.allocate(1024);
System.out.println("-----------------allocate()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
buf.put(str.getBytes());
System.out.println("-----------------put()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
buf.flip();
System.out.println("-----------flip(): limit = position,position=0----------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
byte[] dst = new byte[buf.limit()];
buf.get(dst);
System.out.println(new String(dst, 0, dst.length));
System.out.println("-----------------get()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
buf.rewind();
System.out.println("-----------------rewind,只重置position归0()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
buf.clear();
System.out.println("-------clear,position重置归0,limit=capacity()----------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
System.out.println((char)buf.get());
System.out.println("-------reset,position = mark----------");
}
}
public void test2(){
String str = "itheima";
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.put(str.getBytes());
buf.flip();
byte[] dst = new byte[buf.limit()];
buf.get(dst, 0, 2);
System.out.println(new String(dst, 0, 2));
System.out.println(buf.position());
buf.mark();
buf.get(dst, 2, 2);
System.out.println(new String(dst, 2, 2));
System.out.println(buf.position());
buf.reset();
System.out.println(buf.position());
if(buf.hasRemaining()){
System.out.println(buf.remaining());
}
}
public void test3(){
ByteBuffer buf = ByteBuffer.allocateDirect(1024);
System.out.println(buf.isDirect());
}
直接与非直接内存
根据官方文档的描述:
byte byffer
可以是两种类型,一种是基于直接内存(也就是非堆内存);另一种是非直接内存(也就是堆内存)。
对于直接内存来说,JVM将会在IO操作上具有更高的性能, 因为它直接作用于本地系统的IO操作。
非直接内存,也就是堆内存中的数据,如果要作IO操作, 会先从本进程内存复制到直接内存,再利用本地IO处理。
从数据流的角度,非直接内存是下面这样的作用链:
本地IO-->直接内存-->非直接内存-->直接内存-->本地IO
而直接内存是:
本地IO-->直接内存-->本地IO
很明显,在做IO处理时,比如网络发送大量数据时,直接内存会具有更高的效率。
直接内存使用allocateDirect创建,但是它比申请普通的堆内存需要耗费更高的性能。不过,这部分的数据是在JVM之外的,因此它不会占用应用的内存。
所以呢,当你有很大的数据要缓存,并且它的生命周期又很长,那么就比较适合使用直接内存。只是一般来说,如果不是能带来很明显的性能提升,还是推荐直接使用堆内存。字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其 isDirect() 方法来确定。
直接内存使用场景
-
1 有很大的数据需要存储,它的生命周期又很长 -
2 适合频繁的IO操作,比如网络并发场景
关注夏壹分享发送:资源 获取深入讲解JVM虚拟机课程
原文始发于微信公众号(夏壹分享):JAVA NIO核心之缓冲区(Buffer)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/154863.html