java中内存映射使用

今天结合文件传输,记录一下内存映射方式读写文件,的确很快。网上都是用例子说了一下

MappedByteBuffer类的使用,其实还是那些用法,只是想把封装给大家说说。

基本用法一般给大家的代码如下:


        FileChannel fc = new RandomAccessFile("D:/TEST/test3.txt", "rw").getChannel();
        
        MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, length);
        
        for (int i = 0; i < length; i++) {
            mbb.put((byte) 'a');
        }
 FileChannel fileChannel = null;
    try {
        String filePath = "D:\\temp\\avatar.jpg";
        fileChannel = new RandomAccessFile(new File(filePath), "rw").getChannel();
        MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileChannel.size());
        byte[] bytes = new byte[(int) fileChannel.size()];
        mappedByteBuffer.get(bytes);
        print(bytes);

    } catch (Exception e) {
        e.printStackTrace();
    } 

这里先说写代码,如果我们说大文件,比如说20G的写入,则需要分段写。一次写入的数据量一定要比映射的区间小。

  /**
     * 写入
     * @param src
     * @throws IOException
     */
    public void writeFileBuffer(ByteBuffer src) throws IOException {

        if (randomAccessFile == null) {

            long curLen = fileLen - writeLen;//计算剩余的字节
            if (curLen > size) {
                curLen = size;
            }
            try {
                randomAccessFile = new RandomAccessFile(writeFile, "rw");
               // randomAccessFile.setLength(fileLen);
                FileChannel rafchannel = randomAccessFile.getChannel();
                //mmap 使得jvm堆和pageCache有一块映射空间
                MappedByteBuffer map = rafchannel.map(FileChannel.MapMode.READ_WRITE, writeLen, curLen);  // 1000M的pageCache大小
                map.put(src);
                writeMap = map;
                writeLen += src.limit();
                foceLen=src.limit();

            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {

            if (foceLen + src.limit() > size) {
                //重新加载
                long curLen = fileLen - writeLen;
                if (curLen > size) {
                    curLen = size;
                }
                MappedByteBuffer map = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, writeLen, curLen);  // 1000M的pageCache大小
                map.put(src);
                writeMap = map;
                writeLen += src.limit();
                foceLen = src.limit();
            } else {
                writeMap.put(src);
                foceLen += src.limit();
                writeLen += src.limit();
            }
        }

    }

如果size大小的映射空间不够类,就需要从已经写入的大小(当前文件末尾),从新映射区间,然后继续写入。文件快写完时,也不能太多,多余的就是空的,二进制文件就麻烦了。要计算刚刚好。在这里传输的时候,先通知对方传输的文件名称和大小,这样就好了。

foceLen用了记录映射区间已经使用的大小,新写入不够就重新映射。这样连续写完为止。

读取20G大文件原来一样。

  /**
     * 读取文件
     * @param path
     * @throws IOException
     */
    public void readFile(String path) throws IOException {
        randomAccessFile = new RandomAccessFile(path, "rw");
    }

    /**
     * 读取数据
     * @return
     * @throws IOException
     */
    public ByteBuffer read() throws IOException {

        if (randomAccessFile != null) {

            long pos = readNum * size;//已经读取的字节
            long leftNum = randomAccessFile.getChannel().size() - pos;//剩下字节
            readNum++;
            try {
                if (leftNum > size) {
                    MappedByteBuffer map = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, pos, size);  // 1000M的pageCache大小
                    return map.asReadOnlyBuffer();
                } else {
                    isRead=true;
                    MappedByteBuffer map = randomAccessFile.getChannel().map(FileChannel.MapMode.PRIVATE, pos, leftNum);  // 1000M的pageCache大小
                    randomAccessFile.close();//最后一次了
                    return map.asReadOnlyBuffer();
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
        return null;
    }

每次读取size大小,我这里说1000M。最后一次读取就标记读取完了。

完整类

package MappedByteBufferFile;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class MMapBufferChanel {

    /**
     * 读写缓存
     */
    private final int size = 4096 * 1024 * 250;


    public int getSize() {
        return size;
    }

    private RandomAccessFile randomAccessFile = null;

    /**
     * 读取次数
     */
    private long readNum = 0;

   private  boolean isRead=false;
    /**
     * 写入的文件
     */
    private String writeFile;

    /**
     * 写入的
     */
    private MappedByteBuffer writeMap = null;

    /**
     * 已经写的缓存
     */
    private  long foceLen=0;

    /**
     * 写的文件长度
     */
    private long fileLen;

    /**
     * 已经写入的长度
     */
    private long writeLen = 0;

    /**
     * 读取完成
     * @return
     */
    public boolean getRead()
    {
        return  isRead;
    }

    public void setFileLen(long len) {
        this.fileLen = len;
    }


    public void setWriteFile(String file) {
        this.writeFile = file;
    }
   public  void  close() throws IOException {
       if(randomAccessFile!=null)
       {
           randomAccessFile.close();
       }
   }
    /**
     * 写入
     * @param src
     * @throws IOException
     */
    public void writeFileBuffer(ByteBuffer src) throws IOException {

        if (randomAccessFile == null) {

            long curLen = fileLen - writeLen;//计算剩余的字节
            if (curLen > size) {
                curLen = size;
            }
            try {
                randomAccessFile = new RandomAccessFile(writeFile, "rw");
               // randomAccessFile.setLength(fileLen);
                FileChannel rafchannel = randomAccessFile.getChannel();
                //mmap 使得jvm堆和pageCache有一块映射空间
                MappedByteBuffer map = rafchannel.map(FileChannel.MapMode.READ_WRITE, writeLen, curLen);  // 1000M的pageCache大小
                map.put(src);
                writeMap = map;
                writeLen += src.limit();
                foceLen=src.limit();

            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {

            if (foceLen + src.limit() > size) {
                //重新加载
                long curLen = fileLen - writeLen;
                if (curLen > size) {
                    curLen = size;
                }
                MappedByteBuffer map = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, writeLen, curLen);  // 1000M的pageCache大小
                map.put(src);
                writeMap = map;
                writeLen += src.limit();
                foceLen = src.limit();
            } else {
                writeMap.put(src);
                foceLen += src.limit();
                writeLen += src.limit();
            }
        }

    }


    /**
     * 读取文件
     * @param path
     * @throws IOException
     */
    public void readFile(String path) throws IOException {
        randomAccessFile = new RandomAccessFile(path, "rw");
    }

    /**
     * 读取数据
     * @return
     * @throws IOException
     */
    public ByteBuffer read() throws IOException {

        if (randomAccessFile != null) {

            long pos = readNum * size;//已经读取的字节
            long leftNum = randomAccessFile.getChannel().size() - pos;//剩下字节
            readNum++;
            try {
                if (leftNum > size) {
                    MappedByteBuffer map = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, pos, size);  // 1000M的pageCache大小
                    return map.asReadOnlyBuffer();
                } else {
                    isRead=true;
                    MappedByteBuffer map = randomAccessFile.getChannel().map(FileChannel.MapMode.PRIVATE, pos, leftNum);  // 1000M的pageCache大小
                    randomAccessFile.close();//最后一次了
                    return map.asReadOnlyBuffer();
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
        return null;
    }
}

全部是ByteBuffer是因为我使用的是sockchannel。这里虽然读写一个类,但是使用必须是不同实例。

如果只是读取20G一瞬间。写入速度不稳,笔记本机械硬盘70M-100M.我使用的还有传输阻塞,传完就读完。传输阻塞慢,所以读取就和传输一样。如果内存足够异步读取,那就说读完很久才传输完。sock缓存设置10M,传输130M/s.

完整的代码会上传csdn.

git地址:

https://github.com/jinyuttt/MappedByteBufferFile.git

你可能感兴趣的:(java,文件内存映射,高速传输)