Java File.createNewFile()比FileOutputStream慢得多

时间:2017-06-20 09:04:24

标签: java

使用File.createNewFile()或File.createTempFile()时,我观察到一个有趣的性能下降。以下代码创建48个线程,每个线程将大约128MB的数据写入不同的文件。如果我按原样运行代码,我的特定机器上大约需要60秒。如果我完全按原样运行代码,除了我注释掉f.createTempFile()调用之外,它需要大约5秒钟。

import java.util.*;
import java.util.concurrent.*;
import java.io.File;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public final class TestFile implements Runnable {
  public void run() {
    byte[] b = new byte[128205100];
    Arrays.fill(b, (byte)10);

    try {
      File f = new File("/tmp/test", UUID.randomUUID().toString());

      // If I comment the following f.createNewFile() then the code takes
      // 5 seconds rather than 60 to execute. 
      f.createNewFile();

      FileOutputStream fOutputStream =  new FileOutputStream(f);
      BufferedOutputStream fBufStream = new BufferedOutputStream(fOutputStream, 32768);
      fBufStream.write(b);
      fBufStream.close();
    } catch (IOException e) {
      System.err.println("Caught IOException: " + e.getMessage());
    }
  }

  public static void main(String[] args) {
    final ExecutorService executorPool = Executors.newFixedThreadPool(48);
    for (int counter=0; counter < 48; counter++) {
       executorPool.execute(new TestFile());
    }
    try {
      executorPool.shutdown();
      executorPool.awaitTermination(120, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
      System.err.println("Caught InterruptedException: " + e.getMessage());
    }       
  }
}

使用jstack,我可以看到,当运行上面的代码时,所有线程最终都将大部分时间花在close0()上。遗憾的是,这个函数是原生的: - /我知道在哪里找到它的来源?

"Thread-47" #68 prio=5 os_prio=0 tid=0x00007f21001de800 nid=0x4eb4 runnable [0x00007f209edec000]
   java.lang.Thread.State: RUNNABLE
        at java.io.FileOutputStream.close0(Native Method)
        at java.io.FileOutputStream.access$000(FileOutputStream.java:53)
        at java.io.FileOutputStream$1.close(FileOutputStream.java:356)
        at java.io.FileDescriptor.closeAll(FileDescriptor.java:212)
        - locked <0x00000005908ad628> (a java.io.FileDescriptor)
        at java.io.FileOutputStream.close(FileOutputStream.java:354)
        at java.io.FilterOutputStream.close(FilterOutputStream.java:159)
        at TestFile.run(TestFile.java:19)
        at java.lang.Thread.run(Thread.java:745)

我的猜测是某个地方(在本地close0内?)正在发出同步,但我找不到它。我在几台机器上测试了这个,其中一些机器我没有看到它的退化。所以这可能是基于配置或环境的。

我使用Java 8在Ubuntu上运行。

非常感谢任何帮助。谢谢!

1 个答案:

答案 0 :(得分:1)

这非常简单。 File.createNewFile()按该名称搜索文件,如果文件不存在则会创建新文件,或者您无法正确忽略,因为它至少无关紧要成功与否。 new FileOutputStream()以相同名称搜索任何现有文件,删除它并创建新文件。

因此,File.createNewFile()显然是new FileOutputStream()完全浪费时间,因为它迫使操作系统:

  1. 搜索文件。
  2. 创建它,如果它不存在或失败。
  3. 搜索文件。
  4. 删除它(如果存在)。
  5. 创建它。
  6. 显然,(1)和(2)是浪费时间,而力(4)在没有必要的时候发生。

    解决方案:在File.createNewFile()之前不要致电new FileOutputStream(...)。或者new FileWriter(...)或者new PrintStream/PrintWriter(...)。没有什么可以获得,浪费时间和空间。