使用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上运行。

任何帮助将不胜感激。谢谢!

最佳答案

非常简单。 File.createNewFile()用该名称搜索文件,然后创建一个新文件(如果不存在)或失败(您正确忽略了该文件),因为至少成功与否无关紧要。 new FileOutputStream()搜索具有相同名称的任何现有文件,将其删除,然后创建一个新文件。

因此,很明显,File.createNewFile()后面紧跟new FileOutputStream()完全是浪费时间,因为它迫使操作系统执行以下操作:


搜索文件。
如果它不存在或失败,则创建它。
搜索文件。
删除它(如果存在)。
创造它。


显然,(1)和(2)浪费时间,并且在不需要时强制(4)发生。

解决方案:请勿在File.createNewFile()之前呼叫new FileOutputStream(...)。或new FileWriter(...)new PrintStream/PrintWriter(...)。没有什么可以得到的,浪费时间和空间。

07-24 21:51