java - 如何在不创建对象的情况下找出文件和目录的大小?

Posted

技术标签:

【中文标题】java - 如何在不创建对象的情况下找出文件和目录的大小?【英文标题】:how to find out the size of file and directory in java without creating the object? 【发布时间】:2011-05-02 12:37:26 【问题描述】:

首先请不要忽视,因为您可能认为这是常见问题,但事实并非如此。我知道如何使用file.length 和Apache FileUtils.sizeOfDirectory 找出文件和目录的大小。

我的问题是,在我的情况下,文件和目录太大(数百 mb)。当我尝试使用上面的代码(例如创建文件对象)找出大小时,我的程序会变得非常消耗资源并降低性能。

有没有办法在不创建对象的情况下知道文件的大小?

我正在使用 对于文件 File file1 = new file(fileName);长尺寸 = file1.length();

对于目录,文件 dir1 = 新文件 (dirPath);长尺寸 = fileUtils.sizeOfDirectiry(dir1);

我有一个参数可以进行尺寸计算。如果参数为假,那么它会顺利进行。如果为 false,则程序滞后或挂起。我正在计算 4 个目录和 2 个数据库文件的大小。

【问题讨论】:

A File 对象不会将文件读入内存,AFAIK。所以应该不存在资源问题。 什么是fileUtils.sizeOfDirectory?它是内部实用程序还是第 3 方? @Ronk 来自 apache commons commons.apache.org/io/api-1.1/org/apache/commons/io/… 我不确定它是否会加载到内存中,但它会给我带来很大的性能影响:( 【参考方案1】:

文件对象非常轻量级。您的代码有问题,或者问题不在于文件对象,而在于获取文件大小所需的 HD 访问权限。如果您对大量 文件(例如,数万个)执行此操作,那么硬盘将执行大量搜索,这几乎是现代 PC 上可能最慢的操作(几个数量级)。

【讨论】:

我认为是的,可能是因为磁盘操作。我研究了代码,但没有发现任何打开的文件都关闭了。可能是问题所在。让我检查一下.. @swd:你不需要打开文件来获取它们的大小。您的代码在做什么 文件 file1 = new File(fileName);长尺寸 = file1.length(); @swd:好的,这是正确的做法,但是文件没有打开。 对于目录,文件 dir1 = 新文件 (dirPath);长尺寸 = fileUtils.sizeOfDirectiry(dir1);我有一个参数可以进行尺寸计算。如果参数为假,那么它会顺利进行。如果为假,则程序滞后或挂起。我正在计算 4 个目录和 2 个数据库文件的大小。【参考方案2】:

文件只是文件路径的包装。文件有多大并不重要,只是它的文件名。

当您想获取目录中所有文件的大小时,操作系统需要读取目录,然后查找每个文件以获取其大小。每次访问大约需要 10 毫秒(因为这是硬盘驱动器的典型寻道时间)因此,如果您有 100,000 个文件,则需要大约 17 分钟才能获得所有大小。

加快速度的唯一方法是获得更快的驱动器。例如固态硬盘的平均寻道时间为 0.1 毫秒,但仍需要 10 秒或更长时间才能获得 100K 文件的大小。

顺便说一句:每个文件的大小并不重要,因为它实际上并不读取文件。只有具有其大小的文件条目。


编辑:例如,如果我尝试获取大目录的大小。一开始速度很慢,但缓存数据后速度会快得多。

$ time du -s /usr
2911000 /usr

real    0m33.532s
user    0m0.880s
sys 0m5.190s

$ time du -s /usr
2911000 /usr

real    0m1.181s
user    0m0.300s
sys 0m0.840s

$ find /usr | wc -l
259934

第一次查找速度如此之快的原因是所有文件都立即安装,并且大部分信息在磁盘上连续可用。一旦信息在内存中,读取文件信息几乎不需要时间。

Timing FileUtils.sizeOfDirectory("/usr") 耗时不到 8.7 秒。与 du 所花费的时间相比,这相对较慢,但它每秒处理大约 30K 个文件。

一种替代方法可能是运行Runtime.exec("du -s "+directory);,但是,这最多只会产生几秒钟的差异。如果磁盘不在缓存中,大部分时间可能会花在等待磁盘上。

【讨论】:

@Peter Lawrey:这些数字没有意义。我有一台标准的戴尔笔记本电脑,我|只是让 windows 给我Windows 目录的大小 - 它给了我 10.7GB、57K 文件 + 11K 目录,这花了不到 30 秒。 @RonK,缓存有很大的不同。在我看来,它将以低于 3K 的速度访问磁盘(一个典型的笔记本电脑驱动器需要 12 毫秒来进行一次寻道),因为操作系统经常读入超过它需要的数据,希望你需要它稍后提供信息。例如它通常读取 32KB,即使一个典型的文件条目可能是 512 字节长。这意味着对于每次阅读,您实际上已经加载了 64 个条目,这在以后可能会有用。如果你再次做同样的事情,它应该会快得多。 如果您投反对票但不发表评论,这就像说我知道您做错了什么,但我保持沉默。我们都愿意学习,我很欣赏建设性的批评。 @Peter Lawrey:缓存确实很有帮助,现在花了 3 秒。尽管如此,这些数字表明访问速度比上述问题中指示的要快得多 - 再次在我的笔记本电脑上,57GB 的文件(200K 的文件) - 不到 1 分钟来计算和求和,所以当 apache 通用 IO 包出现性能问题时与操作系统相比,或其他原因导致性能差异 您已经确定了文件的访问方式以及它们是否在内存中会对性能产生 10 倍的影响。 Windows 目录通常是一次性安装的,并且在一小部分更新中进行安装。这意味着它可以相当优化地安排。也因为它是操作系统的一部分,很大一部分已经在缓存中。如果要进行比较,则需要在同一目录上运行 Apache FileUtils.sizeOfDirectory。【参考方案3】:

我们在包含大量文件的目录中使用 File.listFiles() 时遇到了类似的性能问题。

我们的设置是一个包含 10 个子文件夹的文件夹,每个文件夹包含 10,000 个文件。 该文件夹在网络共享上,而不是在运行测试的机器上。

我们使用 FileFilter 只接受具有已知扩展名或目录的文件,因此我们可以追索这些目录。

分析显示,大约 70% 的时间用于调用 File.isDirectory(我假设 Apache 正在调用)。每个文件有两次对 isDirectory 的调用(一次在过滤器中,一次在文件处理阶段)。

File.isDirectory 很慢,因为它必须访问每个文件的网络共享。

将过滤器中的检查顺序颠倒,在有效目录之前检查有效名称节省了大量时间,但我们仍然需要调用 isDirectory 进行递归查找。

我的解决方案是在本机代码中实现一个 listFiles 版本,这将返回一个包含有关文件的所有元数据的数据结构,而不仅仅是像 File 那样的文件名。

这消除了性能问题,但增加了必须由 Java 开发人员维护的本机代码的维护问题(幸运的是,我们只支持一个操作系统)。

【讨论】:

对,我有同样的问题.. 我的文件夹也在网络上.. Stackr 建议使用 Files 类,它为您提供文件的元数据信息.. 但我们将不得不等到那个时候..另外我担心没有目录支持会再次导致我们调用 listFiles !!【参考方案4】:

我认为您需要读取文件的元数据。 阅读本教程以获取更多信息。这可能是您正在寻找的解决方案: http://download.oracle.com/javase/tutorial/essential/io/fileAttr.html

【讨论】:

我不能有“文件”类!在哪个包中声明它 那是在 JAva 7.. 我使用 java6.. :(【参考方案5】:

回答我自己的问题..

这不是最好的解决方案,但适用于我的情况..

我创建了一个批处理脚本来获取目录的大小,然后在 java 程序中读取它。当目录中的文件数量超过 1L 时,它给了我更少的执行时间(在我的情况下总是如此).. sizeOfDirectory 大约需要 30255 毫秒,而使用批处理脚本我得到 1700 毫秒.. 对于更少的文件,批处理脚本的成本很高.

【讨论】:

【参考方案6】:

我将添加 Peter Lawrey 的回答并补充说,当一个目录中有很多文件时(直接,而不是在子目录中) - file.listFiles() 花费的时间非常慢(我不有确切的数字,我从经验中知道)。如果我没记错的话,文件的数量必须很大,几千个 - 如果这是你的情况,fileUtils 会做的实际上是尝试一次将所有他们的名字加载到内存中 - 这可能会很消耗。

如果这是您的情况 - 我建议重组目录以具有某种层次结构,以确保每个子目录中的文件数量较少。

【讨论】:

以上是关于java - 如何在不创建对象的情况下找出文件和目录的大小?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不覆盖之前数据的情况下创建多个 java txt 文件? [复制]

如何在不暴露自己的情况下找出宏的作用?

如何在不使用内置函数的情况下计算数字的平方根? [重复]

StringBuffer 如何在不创建两个对象的情况下实现 append 功能?

如何在不重置Java的情况下绘制多个对象

如何在不创建临时对象的情况下迭代嵌套的 TreeMap