allocateDirect会通过C/C++的native方法创建堆外内存,java虚拟机无法直接操作.
/**
* Allocates a new direct byte buffer.
*
*
The new buffer's position will be zero, its limit will be its
* capacity, its mark will be undefined, and each of its elements will be
* initialized to zero. Whether or not it has a
* {@link #hasArray backing array} is unspecified.
*
* @param capacity
* The new buffer's capacity, in bytes
*
* @return The new byte buffer
*
* @throws IllegalArgumentException
* If the capacity is a negative integer
*/
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
是通过address属性来访问堆外内存的
public abstract class Buffer {
/**
* The characteristics of Spliterators that traverse and split elements
* maintained in Buffers.
*/
static final int SPLITERATOR_CHARACTERISTICS =
Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
// Used only by direct buffers
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress
long address;
hosted是升级的意思.
因为按道理来说address应该放到DirectByteBuffer当中,但是却放到了他的父类Buffer当中,所以这叫做升级.
是为了加快JNI方法调用.
address表示的是堆外内存的地址.
不直接在堆上面分配内存,而要在堆外分配内存,可以免去java堆和OS native堆之间的数据交互,实现零拷贝,zeroCopy.
为什么一定要把数据从java堆里面拷贝出来呢?为什么OS不直接使用堆里面的数据呢?因为java堆里面的数据,有可能出现GC,当出现GC的时候,数据的内存地址就发生了变化,如果OS在使用数据的时候发生GC的话,整个就乱套了.所以必须要拷贝一份到OS内核中.
JVM会保证在拷贝过程中不会发生GC
DirectByteBuffer被回收的时候,相应的堆外内存也会被回收,不会出现内存泄露的情况.
public class readAndWrite1 {
/**
* allocateDirect返回的是一个DirectByteBuffer
* 那么DirectByteBuffer相比于HeapByteBuffer有什么不同
* 1.HeapByteBuffer这个对象是存在堆上。而DirectByteBuffer,在使用堆内存的同时还会使用堆外内存,他这个对象在堆上,而buffer数组在堆外内存。
* 2.堆外内存:即调用native方法,使用C或C++编写的方法处理数据。(在堆外,但是我们java是在堆内存啊,要使用堆外内存怎么办)
* 3.在DirectByteBuffer中有个long类型的address指向堆外内存,那么凭借这个我们就可以连接两块内存。
* 那为啥要用到堆外内存?
* 1.普通的在堆上分配的内存,在操作系统进行读取时,需要先将堆内存复制一份到操作系统,这样速度就受限了。(这种复制情况也是受限于需要对IO设备进行交互,例如文件读取等)
* (疑问:为啥操作系统需要复制一份数据,不可以直接读取吗?可以,但是不能这样。主要原因是java有垃圾回收机制,操作系统可能就在读取数据时发生了垃圾回收
* 其中标记整理法会将内存压缩,这样不就打乱了数组顺序吗?所以操作系统复制一份进行操作.
* 因此DirectByteBuffer在堆外分配,就不需要操作系统拷贝了,也被称为0拷贝.
* )
* 2.如果使用堆外内存,操作系统直接读取,速度超快。
* 总结:DirectByteBuffer(直接缓冲区),直接本地IO操作,避免了java虚拟机复制一块中间缓冲区。
* * @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
FileInputStream in=new FileInputStream("input.txt");
FileOutputStream out=new FileOutputStream("out.txt");
FileChannel fileInChannel=in.getChannel();
FileChannel fileOutChannel=out.getChannel();
ByteBuffer byteBuffer=ByteBuffer.allocateDirect(512);
while (true){
byteBuffer.clear();
int read=fileInChannel.read(byteBuffer);
System.out.println("read:"+read);
if ( -1 == read){
break;
}
byteBuffer.flip();
fileOutChannel.write(byteBuffer);
}
fileInChannel.close();
fileOutChannel.close();
}
}