使用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(...)
。没有什么可以得到的,浪费时间和空间。