JAVA NIO的相关方法以及使用
普通方法简介类型化的put 以及类型化的get 方法 他们的底层都是对字节数组的操作
**
* 描述:
* byte 方法的
* 类型化put和类型化get
*
* @author HeGaoJian
* @version 1.0
* @create 2019-01-11 15:21
*/
public class NIOTest5 {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(64);
byteBuffer.putInt(15);
byteBuffer.putLong(5000000000L);
byteBuffer.putDouble(3.14159265357985);
byteBuffer.putChar('款');
byteBuffer.putShort((short) 2);
byteBuffer.putChar('和');
byteBuffer.flip();
System.out.println(byteBuffer.getInt());
System.out.println(byteBuffer.getLong());
System.out.println(byteBuffer.getDouble());
System.out.println(byteBuffer.getChar());
System.out.println(byteBuffer.getShort());
System.out.println(byteBuffer.getChar());
}
}
sliceBuffer 的分片操作 Demo
概念: 对里面的数据任何一次修改都会到新的Buffer上面
SliceBuffer 与原有Buffer 共享数据 相同的底层数组
两个Buffer的值是独立的
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
for (int i = 0; i < byteBuffer.capacity(); i++) {
byteBuffer.put((byte) i);
}
byteBuffer.position(2);
byteBuffer.limit(5);
ByteBuffer sliceBuffer = byteBuffer.slice();
//操作sliceBuffer
for (int i = 0; i < sliceBuffer.capacity(); i++) {
//取出来里面的元素
byte newbyte = sliceBuffer.get(i);
//每一个元素乘以2
newbyte *= 2;
//添加到buffer
sliceBuffer.put(i, newbyte);
}
//调出原有的buffer 并遍历原有的Buffer
//是一个绝对的操作
byteBuffer.position(0);
byteBuffer.limit(byteBuffer.capacity());
//原有的Buffer也同样更改
while (byteBuffer.hasRemaining()) {
System.out.println(byteBuffer.get());
}
}
只读的Buffer
/**
* 描述:
* 只读buffer 只能读buffer 的 随时将一个普通buffer调用asreadOnlybuffer
* 但是不能将一个只读buffer转化为一个读写buffer
*
* @author HeGaoJian
* @version 1.0
* @create 2019-01-11 16:09
*/
public class ReadOnlyBuffer {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(10);
System.out.println(buffer.getClass());
for (int i = 0; i < buffer.capacity(); i++) {
buffer.put((byte) i);
}
ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();
//class java.nio.HeapByteBufferR
System.out.println(readOnlyBuffer.getClass());
readOnlyBuffer.position();
//这个就是试着更改它结果报错了 要报错喽
// readOnlyBuffer.put((byte) 2);
}
HeapByteBuffer 与DirectByteBuffer
我们在初始化Buffer的时候看到是有两种方法的 :
//追源码我们发现 allocate 构造出来的是DirectByteBuffer 对象
ByteBuffer buffer = ByteBuffer.allocateDirect(512);
//追源码我们发现 allocate 构造出来的是HeapByteBuffer 对象
ByteBuffer allocate = ByteBuffer.allocate(512);
那么它们的区别是什么呢?
DirectByteBuffer 用到的对象是sun包下面的也就是闭源的 里面也有native 本地的方法
也就是直接缓存 与 间接缓存
DirectByteBuffer ,是存在于java堆里面的,new出来的对象是由在java领域下面的 JVm所掌控的
native 不在java内存中间, 也就是堆外内存 , 由c 或者c++ 来生成的 , 也就是walloc 方法
那么 DirectByteBuffer 里面一定有一个成员变量可以访问堆外内存的 那就是
java.nio.Buffer 中的address address表示就是在堆外内存的地址
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress
long address;
HeapByteBuffer 是由操作系统将JVM 中的字节数组拷贝到java内存之外的内存也就是操作系统的内存
如果使用的是 DirectByteBuffer 的话是程序直接与堆外内存进行操作, (再把数据取出来在对比与操作等等) 也就是由JNI操作的
这种操作方式呢,我们就叫做0拷贝 是不是很这个词熟悉??
but为什么要拷贝, IO操作的时候 通过JNI的方式访问 操作JVM内存 ,
可能发生Gc算法等, (例如先标记在压缩的算法过程)如果Native的操作过程中发生了压缩那么数据就乱套了,所以不能进行GC操作
所以我们要进行拷贝的操作与过程(拷贝过程不会发生GC)
这样我们就0拷贝到的方式进行了操作
源码分析
java.nio.DirectByteBuffer#DirectByteBuffer(int)
//内存分配
base = unsafe.allocateMemory(size);
java.nio.DirectByteBuffer.Deallocator#Deallocator
//内存释放
unsafe.freeMemory(address);
//Copy的方法 也就Native本地的方法
sun.misc.Unsafe#copyMemory(java.lang.Object, long, java.lang.Object, long, long)
总结
HeapByteBuffer 是直接内存模型 ,JAVA 堆上的操作 必须要经过一个Native 堆, 在由Native多进行了一个拷贝操作,直接操作内存
JAVA 堆外内存