如何在java中列出一个200万个文件目录,而不使用;“内存不足”;例外

如何在java中列出一个200万个文件目录,而不使用;“内存不足”;例外,java,file,file-io,out-of-memory,Java,File,File Io,Out Of Memory,我必须处理一个大约有200万xml的目录 我已经解决了使用队列在机器和线程之间分配工作的处理问题,一切正常 但现在最大的问题是读取包含200万个文件的目录的瓶颈,以便以增量方式填充队列 我尝试过使用File.listFiles()方法,但它给了我一个java内存不足:堆空间异常。有什么想法吗?首先,您可以尝试通过传递-Xmx1024m来增加JVM的内存,例如,为什么要在同一个目录中存储200万个文件?我可以想象,在操作系统级别上,它已经严重降低了访问速度 我肯定希望在处理之前将它们划分为子目录(

我必须处理一个大约有200万xml的目录

我已经解决了使用队列在机器和线程之间分配工作的处理问题,一切正常

但现在最大的问题是读取包含200万个文件的目录的瓶颈,以便以增量方式填充队列


我尝试过使用
File.listFiles()
方法,但它给了我一个java
内存不足:堆空间
异常。有什么想法吗?

首先,您可以尝试通过传递-Xmx1024m来增加JVM的内存,例如,

为什么要在同一个目录中存储200万个文件?我可以想象,在操作系统级别上,它已经严重降低了访问速度


我肯定希望在处理之前将它们划分为子目录(例如,按创建日期/时间)。但是,如果由于某种原因不可能,是否可以在处理过程中进行?例如,将排队等待Process1的1000个文件移动到Directory1,将另外1000个等待Process2的文件移动到Directory2等。然后每个进程/线程只看到为其分配的(数量有限的)文件。

请发布OOM异常的完整堆栈跟踪,以确定瓶颈在哪里,以及一个短消息,显示您看到的行为的完整Java程序


这很可能是因为你在内存中收集了200万个条目,但它们都不合适。你能增加堆空间吗?

首先,你有没有可能使用Java 7?这里有一个
FileVisitor
Files.walkFileTree
,它们可能在内存限制范围内工作

否则,我唯一能想到的方法就是使用
File.listFiles(FileFilter filter)
和一个总是返回
false
(确保文件的完整数组永远不会保存在内存中)的过滤器,但它会捕获要处理的文件,并可能将它们放入生产者/消费者队列,或将文件名写入磁盘以供以后遍历

或者,如果您控制文件的名称,或者如果它们的命名方式很好,您可以使用一个过滤器将文件分块处理,该过滤器接受以下格式的文件名:
file0000000
-
filefile0001000
然后
file0001000
-
file0002000
等等

如果名称的命名方式不是这样的好,您可以尝试根据文件名的哈希代码对其进行过滤,该哈希代码应该相当均匀地分布在整数集上


更新:叹息。可能不行。刚刚查看了listFiles实现:

public File[] listFiles(FilenameFilter filter) {
    String ss[] = list();
    if (ss == null) return null;
    ArrayList v = new ArrayList();
    for (int i = 0 ; i < ss.length ; i++) {
        if ((filter == null) || filter.accept(this, ss[i])) {
            v.add(new File(ss[i], this));
        }
    }
    return (File[])(v.toArray(new File[v.size()]));
}
public File[]列表文件(FilenameFilter过滤器){
字符串ss[]=list();
if(ss==null)返回null;
ArrayList v=新的ArrayList();
对于(int i=0;i
所以不管怎样,它可能会在第一行失败。。。有点令人失望。我相信你最好的选择是把文件放在不同的目录中

顺便说一句,你能举个文件名的例子吗?它们是“可猜测的”吗?像

for(int i=0;i<100000;i++)
tryToOpen(String.format(“文件%05d”,i))
使用而不是
File.listFiles()
-它返回的
字符串
对象比
文件
对象消耗更少的内存,而且(更重要的是,取决于目录的位置)它们不包含完整的路径名

然后,在处理结果时根据需要构造
文件
对象


但是,这也不适用于任意大的目录。总的来说,将文件组织在目录的层次结构中是一个更好的主意,这样没有一个目录的条目超过几千个。

如果文件名遵循某些规则,您可以使用
file.list(filter)
而不是
file.listFiles
来获得文件列表中可管理的部分。

试试看,这对我很有用,但是我没有那么多的文件

File dir = new File("directory");
String[] children = dir.list();
if (children == null) {
   //Either dir does not exist or is not a  directory
  System.out.print("Directory doesn't  exist\n");
}
else {
  for (int i=0; i<children.length; i++) {   
    // Get filename of file or directory   
    String filename = children[i];  
}
File dir=新文件(“目录”);
String[]children=dir.list();
if(children==null){
//目录不存在或不是目录
System.out.print(“目录不存在\n”);
}
否则{

对于(int i=0;i,如果Java 7不是一个选项,那么这种破解方法将有效(对于UNIX):

-f参数将使其加速(从
man ls
):


由于您使用的是Windows,似乎您应该简单地使用ProcessBuilder启动“cmd/k dir/b target_directory”之类的程序,捕获该程序的输出,并将其路由到一个文件中。然后您可以一次处理一行文件,读取文件名并进行处理


迟做总比不做好?;)

如果可以使用Java 7,可以通过这种方式完成,并且不会出现内存不足的问题

Path path = FileSystems.getDefault().getPath("C:\\path\\with\\lots\\of\\files");
        Files.walkFileTree(path, new FileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                // here you have the files to process
                System.out.println(file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
               return FileVisitResult.TERMINATE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
              return FileVisitResult.CONTINUE;
            }
        });
Path Path=FileSystems.getDefault().getPath(“C:\\Path\\with\\lots\\of\\files”);
walkFileTree(路径,newFileVisitor(){
@凌驾
公共文件VisitResult preVisitDirectory(路径目录,基本文件属性属性属性)引发IOException{
返回FileVisitResult.CONTINUE;
}
@凌驾
公共文件VisitResult visitFile(路径文件,基本文件属性属性属性)引发IOException{
//这里有要处理的文件
System.out.println(文件);
返回FileVisitResult.CONTINUE;
}
@凌驾
公共文件VisitResult visitFileFailed(路径文件,IOException exc)引发IOException{
返回文件visitresult.TERMINATE;
}
@凌驾
公共文件VisitResult postVisitDirectory(路径目录,IOException exc)thro
Process process = Runtime.getRuntime().exec(new String[]{"ls", "-f", "/path"});
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while (null != (line = reader.readLine())) {
    if (line.startsWith("."))
        continue;
    System.out.println(line);
}
-f     do not sort, enable -aU, disable -lst
Path path = FileSystems.getDefault().getPath("C:\\path\\with\\lots\\of\\files");
        Files.walkFileTree(path, new FileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                // here you have the files to process
                System.out.println(file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
               return FileVisitResult.TERMINATE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
              return FileVisitResult.CONTINUE;
            }
        });
  Iterator<File> it = FileUtils.iterateFiles(folder, null, true);
  while (it.hasNext())
  {
     File fileEntry = (File) it.next();
  }
        Process process = Runtime.getRuntime().exec("ls -R /");
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));

        //TODO: Read the stream to get a list of file path.
Path dir = Paths.get("/some/directory");
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
    for (Path path : stream) {
        handleFile(path.toFile());
    }
} catch (IOException e) {
    handleException(e);
}