java Nio堆外内存和零拷贝

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();
    }
}

 

 

 

 

 

 

 

 

你可能感兴趣的:(java Nio堆外内存和零拷贝)