标签:
Java 1.4中在java.nio包中增加了Buffer类以及一些处理基本数据类型的子类(除了boolean型) ,用来提供为基本数据类型(primitive) 的数据提供一个容器。
何谓Buffer? Buffer 是一个线性的有限长度的特定基本数据的序列。 除了基础数据外,它还包括一些基础操作和属性, 比如capacity, limit 和 position。
实际使用中使用特定的子类来处理数据。每个子类都定义了两套get/put的操作。
数据也可以通过Channel的I/O操作如write,read 写入或者读出。
显然, Buffer只有写入了数据才可能有意义的数据读出。
Buffer类并不是线程安全的, 使用时要特别小心, 避免多线程同时读写同一个Buffer。 万不得已, 需要为读写操作加锁。
cache和buffer的区别
从应用场景上看:Buffer 更多的(场景)是减小写操作的冲击,而 Cache 主要用于减小读 I/O 的重复开销。
Buffer提供了一系列的操作缓冲区的方法以及属性。 但是属性(property)不是以字段field的方式提供,而是以方法method的方式提供。
clear,flip,limit(newLimit),mark,position(newPosition), reset()和rewind返回本身的Buffer, 这意味着你可以使用流式风格, 如
buffer.flip().position(23).limit(42);
请记住以下公式, 下面的不等式在任何时候都成立:
0 <= mark <= position <= limit <= capacity
一个新创建的Buffer的position总是0, mark未定义。 初始的limit可能为0,或者其它正值, 这依赖于buffer的类型以及它是如何创建的。 初始化的Buffer包含零个元素。
另外, Buffer还提供其它的一些成员:
equals()
当满足下列条件时,表示两个Buffer相等:
compareTo()
compareTo()方法比较两个Buffer的剩余元素(byte、char等), 如果满足下列条件,则认为一个Buffer“小于”另一个Buffer:
Buffer是一个抽象类。 实际我们使用的是它的子类, 主要是针对基本数据类型做的优化。
提供了get/put 单个字节或者字节数组的方法, 还是分相对和绝对操作。
字节是其它基本数据类型的基础。 比如int类型是32位也就是4个字节。
Data Type | size (byte) |
---|---|
byte | 1 |
short | 2 |
int | 4 |
long | 8 |
float | 4 |
double | 8 |
char | 2 |
所以可以将这些基本数据类型的数据写入到字节缓冲区中或者从中读出。
ByteBuffer针对基本数据类型定义了便利的方法, 如getChar(),getInt(),putFloat,putShort ...等方法。
注意putXXX可能会抛出BufferOverflowException和ReadOnlyBufferException异常, getXXX可能会抛出BufferUnderflowException异常。
同时ByteBuffer还提供了创建视图view的方法。 可以基于ByteBuffer创建其它基本类型的buffer,它们的底层数据指向都一个对象,但是相应的position, limit, mark都是独立的。例如asIntBuffer()返回一个IntBuffer对象, 返回的IntBuffer对象的第一个元素对应于此ByteBuffer的position的位置的元素。 IntBuffer的position的值为0,capacity 和limit是此ByteBuffer的剩余的字节的数量/4 (int是四个字节), mark未定义。 当且仅当ByteBuffer是直接缓冲区时此IntBuffer才是直接缓冲区, 当且仅当ByteBuffer是只读的 IntBuffer才是只读的。
注意ByteBuffer依然是抽象类, allocate方法和allocateDirect方法创建缓冲区时实际是创建HeapByteBuffer或者DirectByteBuffer类。
仍然支持流式风格。 bb.putInt(0xCAFEBABE).putShort(3).putShort(45);
asReadOnlyBuffer()转换成只读缓冲区。新缓冲区的position, limit, 和 mark是独立的。 初始值和原缓冲区相同。
duplicate复制当前的ByteBuffer,底层的数据是公用的,但是position,capacity,limit ,mark是独立的, 方法返回的ByteBuffer初始拥有和原ByteBuffer相同的position,capacity,limit ,mark。
slice()也是一个新的byte buffer,和原bye buffer的数据共享。 但是新的byte buffer将自原byte buffer的position的位置开始。 这也就是slice的含义。
提供了allocate(int capacity)和allocateDirect(int capacity)两种方法。
wrap(byte[] array), wrap(byte[] array, int offset, int length)将字节数组包装成ByteBuffer。
order()和order(ByteOrder bo)用来返回和设置字节序: 大端模式(BIG_ENDIAN)和小端模式(LITTLE_ENDIAN)。 默认总是大端模式(BIG_ENDIAN)。
compact: 压缩ByteBuffer。 将position和limit的之间的数据复制到缓冲区的开始部分。 比如另p = position,则将 p + 1处的数据复制到index 1, ...... limit -1处的数据复制到n = limit -1 -p。 缓冲区的position设置为n+1, limit设置为capacity, mark丢弃.
以下的子类类似ByteBuffer,但是没有转换成其它Buffer的方法和视图。这是容易理解的,因为Byte才是其它基本数据类型的基础单位。
wrap包装相应基本数据类型的数组。依然有compact, duplicate, slice方法. 流式风格, get/put 单数据操作和批操作, 相对位置操作和绝对位置操作。
提供了append方法,等同于put方法。
charAt(int index)返回指定位置的字符。
subSequence(int start, int end)返回指定位置的缓冲区。 与原缓冲区共享数据。 capacity相同。 新缓冲区的position为原缓冲区position + start, limit为原缓冲区的position + end。 direct, readonly和原缓冲区相同。
处理double类型数据。
处理float类型数据。
处理int类型数据。
处理long类型数据。
处理short类型数据。
继承于ByteBuffer。 它是以内存镜像文件为基础的直接字节缓冲区。 可以通过FileChannel.map创建。
force()强制对数据的改变写入到存储设备。
isLoaded() : 缓冲区的数据是否都全部加载到物理内存中。
load() : 加载缓冲区的数据到物理内存中。
内存映射文件是一种允许Java程序直接从内存访问的特殊文件。通过将整个文件或者文件的一部分映射到内存中、操作系统负责获取页面请求和写入文件,应用程序就只需要处理内存数据,这样可以实现非常快速的IO操作。用于内存映射文件的内存在Java的堆空间以外。
CharBuffer cb = CharBuffer.allocate(1024); ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024);
CharBuffer cb = CharBuffer.allocate(1024); ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024);
FileChannel fc = new RandomAccessFile("test.data", "rw").getChannel(); MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, length);
byte buffer既可以是直接缓冲区可以是非直接缓冲区。 对于直接缓冲区, Java虚拟机极可能的直接执行native I/O操作,避免在操作系统的native I/O操作时还要复制内容到一个中间缓冲区。
它使用Native函数库直接分配堆外内存,然后通过一个存储在JAVA堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
可以通过allocateDirect工厂方法直接创建直接缓冲区, 内部会创建DirectByteBuffer对象, 通过unsafe.allocateMemory分配内存。 相对而言, 这个方法返回的缓冲区要比非直接缓冲区多少有点更高的分配/销毁的花费 (时间和空间)。 直接缓冲区在垃圾回收堆的外部, 所以建议主要用于大的长时间活动的缓冲区,确实能提高性能的环境中。
也可以通过内存镜像文件的方式使用直接缓冲区 FileChannel.map。 Java平台可选择使用JNI来创建直接缓冲区。 如果Buffer指向一个不能访问的内存区域时, 缓冲区的内容不会被更改, 访问操作可能会导致一个不确定的异常。
可以通过isDirect方法判断一个缓冲区是否是直接缓冲区。
虽然直接缓冲区是堆外内存,但是由于DirectByteBuffer引用了它,当DirectByteBuffer被垃圾回收时,此堆外内存会被释放掉,不会出现内存泄漏的问题。
标签:
原文地址:http://my.oschina.net/httpssl/blog/510562