Java 中的 File.exists 有多贵
Posted
技术标签:
【中文标题】Java 中的 File.exists 有多贵【英文标题】:How expensive is File.exists in Java 【发布时间】:2011-09-13 08:27:06 【问题描述】:我想知道File.exists()
是如何工作的。我不太了解文件系统的工作原理,所以我应该先从那里开始阅读。
但对于快速的预信息:
调用File.exists()
是否是文件系统的单个操作,如果该路径和文件名已在某个日志中注册?还是操作系统获取目录的内容,然后扫描它以查找匹配项?
我认为这将取决于文件系统,但也许所有文件系统都使用快速方法?
我不是在谈论网络和磁带系统。让我们把它保存到 ntfs、extX、zfs、jfs :-)
【问题讨论】:
日志文件系统与其他文件系统完全不同。 这将是非常文件系统依赖。当您访问 NFS 或 SMB 文件共享上的文件时,可能涉及建立网络连接。如果底层光盘已断电,您将不得不等待它启动(如果它是光驱则更糟)。哎呀,在某些情况下(例如分层存储),它可能涉及加载磁带并且实际上需要几分钟! 【参考方案1】:测量必要的时间,看看自己。正如你所说,它完全依赖于文件系统。
long t1 = System.currentTimeMillis();
...Your File.exists call
long t2 = System.currentTimeMillis();
System.out.println("time: " + (t2 - t1) + " ms");
你会发现它总是会给你不同的结果,因为它还取决于你的操作系统缓存数据的方式、它的负载等。
【讨论】:
这几乎完全取决于是否需要磁盘访问,在这种情况下,您的磁盘速度(以及它是否繁忙)将是最重要的。如果您在文件在缓存中时进行测试,最好使用 System.nanoTime() 因为它通常是亚毫秒。 System.currentTimeMillis() 甚至不一定给你毫秒分辨率的时间。 :// NanoTime 绝对更精确!仍然 currentTimeMillis 也可能给你很大的想法。也可以在测量之前将系统负载系数插入方程,但结果总是近似的。记住:如果不以某种方式改变它,你就无法观察实验...... 这并不完全取决于 FS 的实现,而是取决于操作系统中文件系统的缓存策略。如果目录被缓存并且文件或目录最近被使用过,您可以获得更快的响应。 为什么你将long
自动装箱到Long
只是为了在减法期间将其转换回long
?【参考方案2】:
如果第一次执行此操作完全取决于文件系统。这是由操作系统完成的,Java 没有任何作用。
就性能而言,在所有情况下都需要读取磁盘。这通常需要 8-12 毫秒。 @Sven 指出一些存储可能会变慢,但在性能很重要的情况下这种情况相对较少。如果这是一个网络文件系统,您可能会有额外的延迟(通常相对较小,但取决于您的网络延迟)。
相比之下,操作系统和 Java 所做的一切都非常短。
但是,如果您反复检查文件是否存在,则可能不需要磁盘访问,因为可以缓存信息,在这种情况下是操作系统占用的时间和资源。 File.exists() 创建的这些对象中最大的一个(你不会认为它会)但是它在每次调用创建大量对象时对文件名进行编码。如果你把 File.exists() 放在一个紧密的循环中,它每秒会产生 400MB 的垃圾。 :(
通过跟踪您对文件系统所做的所有更改,日志文件系统的工作方式有所不同,但它们不会改变您读取文件系统的方式。
【讨论】:
【参考方案3】:大部分与文件相关的操作都不是在 Java 中执行的;存在本机代码来执行这些活动。实际上,完成的大部分工作取决于FileSystem
对象的性质(即支持File
对象)以及操作系统中本机IO 操作的底层实现。
为了清楚起见,我将介绍 OpenJDK 6 中的实现案例。 File.exists() 实现将实际检查推迟到 FileSystem 类:
public boolean exists()
... calls to SecurityManager have been omitted for brevity ...
return ((fs.getBooleanAttributes(this) & FileSystem.BA_EXISTS) != 0);
FileSystem 类是抽象的,所有支持的文件系统都有一个实现:
package java.io;
/**
* Package-private abstract class for the local filesystem abstraction.
*/
abstract class FileSystem
注意包的私有性质。 Java 运行时环境将提供扩展 FileSystem 类的具体类。在 OpenJDK 实现中,有:
java.io.WinNTFileSystem,用于 NTFS java.io.Win32FileSystem,用于 FAT32 java.io.UnixFileSystem,用于 *nix 文件系统(这是一个职责非常广泛的类)。以上所有类都委托给本机代码,用于getBooleanAttributes
方法。这意味着在这种情况下,性能不受托管(Java)代码的限制;文件系统的实现以及所进行的本机调用的性质对性能有更大的影响。
更新 #2
基于更新的问题 -
我不是在谈论网络和磁带系统。让我们把它保存到 ntfs、extX、zfs、jfs
好吧,那仍然没关系。不同的操作系统会以不同的方式实现对不同文件系统的支持。例如,Windows 中的 NTFS 支持将不同于 *nix 中的支持,因为除了通过驱动程序与设备通信外,操作系统还必须进行簿记。并非所有工作都在设备中完成。
在 Windows 中,您几乎总能找到 file system filter drivers 的概念,它管理与其他文件系统过滤器驱动程序或文件系统通信的任务。这是支持各种操作所必需的;一个例子是使用过滤器驱动来拦截 IO 调用。
在 *nix 中,您将拥有 stat() 系统调用,它将执行读取文件描述符的 inode 信息的必要活动。
【讨论】:
【参考方案4】:它在任何现代机器上都非常快,我的测试显示在我的 2013 Mac w/SSD 上为 0.0028 毫秒(2.8 微秒)
在 307 毫秒内创建 1,000 个文件,每个文件 0.0307 毫秒
1,000 .exists() 在 28 毫秒内完成,每个文件 0.0028 毫秒
这是一个在 Groovy (Java) 中的测试
def index()
File fileWrite
long start = System.currentTimeMillis()
(1..1000).each
fileWrite = new File("/tmp/fileSpeedTest/$it.txt")
fileWrite.write('Some nice text')
long diff = System.currentTimeMillis() - start
println "1,000 files created in $diff millis, $diff/10000.0 millis per file"
start = System.currentTimeMillis()
(1..1000).each
fileWrite = new File("/tmp/fileSpeedTest/$it.txt")
if ( ! fileWrite.exists() )
throw new Exception("where's the file")
diff = System.currentTimeMillis() - start
println "1,000 .exists() done in $diff millis, $diff/10000.0 millis per file"
【讨论】:
以上是关于Java 中的 File.exists 有多贵的主要内容,如果未能解决你的问题,请参考以下文章