如何在java中列出一个200万个文件目录,而不使用;“内存不足”;例外
我必须处理一个大约有200万xml的目录 我已经解决了使用队列在机器和线程之间分配工作的处理问题,一切正常 但现在最大的问题是读取包含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万个文件?我可以想象,在操作系统级别上,它已经严重降低了访问速度 我肯定希望在处理之前将它们划分为子目录(
我尝试过使用
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);
}