关于java文件读写io方式

Java常用的几种IO性能对比

最近最常做的工作就是各种文件读写操作,所以来真实的看看,各种Io方式之间的性能对比

FileOutputStream与BufferedWriter

两种区别不大,且与使用NIO来写文件速度区别也不大

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private void writeBuffer(File file) throws IOException {
    FileOutputStream fos = new FileOutputStream(file);
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos));
    int i=1000000;
    while(i>0){
        writer.write(word2048);
        i--;
    }
    writer.close();
    fos.close();
}

ByteBuffer与直接内存

采不采用直接内存几乎没有区别

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
private void byteBuffer(File file) throws IOException {
    FileOutputStream fos = new FileOutputStream(file);
    FileChannel fc = fos.getChannel();
    byte[] datas = word2048.getBytes();
    ByteBuffer bbuf = ByteBuffer.allocate(4800 * 100);
    int i=10000;
    while(i>0){
        for(int j=0;j<100;j++){
            bbuf.put(datas);
        }
        bbuf.flip();
        fc.write(bbuf);
        bbuf.clear();
       i--;
    }
}

FileChannel与文件空洞

NIO中,FileChannel可以决定文件写入的位置,通常用这样来产生文件空洞

MappedByteBuffer

使用直接内存映射,来提升IO性能

这个有一个重大bug,关闭FileChannel后,MappedByteBuffer不会释放所持有的文件。到之后需要如果再对写入的文件进行操作时,会导致没有权限。MappedByteBuffer所持有的内存需要手动来释放。释放方式有几种:

  1. 直接调用System.gc();
  2. 利用反射来释放
1
2
3
4
Method m = FileChannelImpl.class.getDeclaredMethod("unmap",
                    MappedByteBuffer.class);
            m.setAccessible(true);
            m.invoke(FileChannelImpl.class, buffer);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
AccessController.doPrivileged(new PrivilegedAction() {
  public Object run() {
    try {
      Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
      getCleanerMethod.setAccessible(true);
      sun.misc.Cleaner cleaner = (sun.misc.Cleaner) 
      getCleanerMethod.invoke(byteBuffer, new Object[0]);
      cleaner.clean();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
});

2和3两种释放方式,可能会根据jdk的版本而有所不同。(实测1.8是无法通过反射释放内存的)

  • 与传统Io的区别

2024-3-3017:33:26-1711791206165.png

内存文件映射的方式:

2024-3-3017:34:18-1711791258547.png

  • 实例代码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private void mappedByteBuffer(File file) throws IOException {
    RandomAccessFile acf = new RandomAccessFile(file, "rw");
    FileChannel fc = acf.getChannel();
    byte[] bs = word2048.getBytes();
    int len = bs.length * 1000;
    long offset=0;
    int i=2000000;
    while(i>0){
        MappedByteBuffer mbuf = fc.map(FileChannel.MapMode.READ_WRITE, offset, len);
        for(int j=0;j<1000;j++){
            mbuf.put(bs);
        }
        offset=offset+len;
        i=i-1000;
    }
    fc.close();
}

利用apache-common向tar中压缩文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//    打一个tar压缩
    private void tar(File srcDir,String targetFile) throws IOException {
        try(TarArchiveOutputStream tos=new TarArchiveOutputStream(Files.newOutputStream(Paths.get(targetFile)))){
            tarRecursive(tos,srcDir,"");
        }
    }

    private void tarRecursive(TarArchiveOutputStream tos,File srcFile,String BasePath) throws IOException {
//        递归打包文件夹
        if(srcFile.isDirectory()){
            File[] files=srcFile.listFiles();
            String nextBasePath = BasePath + srcFile.getName() + "/";
            if(files==null) {
                TarArchiveEntry entry = new TarArchiveEntry(srcFile, nextBasePath);
                tos.putArchiveEntry(entry);
            }else{
                for (File file : files) {
                    tarRecursive(tos,file,nextBasePath);
                }
            }
            int[][] nums = new int[2][2];
            Arrays.sort(nums, (a, b) -> a[0]-b[0]);
        }else{
            TarArchiveEntry entry=new TarArchiveEntry(srcFile,srcFile.getName());
            tos.putArchiveEntry(entry);
            FileUtils.copyFile(srcFile,tos);
            tos.closeArchiveEntry();
        }
    }
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计