使用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上运行。
非常感谢任何帮助。谢谢!
答案 0 :(得分:1)
这非常简单。 File.createNewFile()
按该名称搜索文件,如果文件不存在则会创建新文件,或者您无法正确忽略,因为它至少无关紧要成功与否。 new FileOutputStream()
以相同名称搜索任何现有文件,删除它并创建新文件。
因此,File.createNewFile()
显然是new FileOutputStream()
完全浪费时间,因为它迫使操作系统:
显然,(1)和(2)是浪费时间,而力(4)在没有必要的时候发生。
解决方案:在File.createNewFile()
之前不要致电new FileOutputStream(...)
。或者new FileWriter(...)
或者new PrintStream/PrintWriter(...)
。没有什么可以获得,浪费时间和空间。