Fgb*_*nch 21 java file-io file out-of-memory
我必须处理一个大约200万xml的目录进行处理.
我已经解决了使用队列在机器和线程之间分配工作的处理,一切正常.
但现在最大的问题是用200万个文件读取目录的瓶颈,以便逐步填充队列.
我尝试过使用该File.listFiles()方法,但它给了我一个java out of memory: heap space异常.有任何想法吗?
aio*_*obe 11
首先,您是否有可能使用Java 7?在那里你有一个FileVisitor和它Files.walkFileTree,它应该在你的记忆约束中起作用.
否则,我能想到的唯一方法是使用File.listFiles(FileFilter filter)一个总是返回的过滤器false(确保完整的文件数组永远不会保存在内存中),但是它会捕获要沿途处理的文件,并且可能会将它们放入生产者/消费者队列或将文件名写入磁盘以供以后遍历.
或者,如果您控制文件的名称,或者它们以某种不错的方式命名,您可以使用在表单上接受文件名的过滤器以块的形式处理文件file0000000- filefile0001000然后file0001000- filefile0002000依此类推.
如果名称没有以这样的好方式命名,您可以尝试根据文件名的哈希码来过滤它们,该哈希代码应该相当均匀地分布在整数集上.
更新:叹息.可能不会起作用.刚看了一下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()]));
}
Run Code Online (Sandbox Code Playgroud)
所以无论如何它可能会在第一线失败......有点令人失望.我相信你最好的选择是将文件放在不同的目录中.
顺便问一下,你能给出一个文件名的例子吗?他们是"可猜测的"吗?喜欢
for (int i = 0; i < 100000; i++)
tryToOpen(String.format("file%05d", i))
Run Code Online (Sandbox Code Playgroud)
如果Java 7不是一个选项,那么这个hack将起作用(对于UNIX):
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);
}
Run Code Online (Sandbox Code Playgroud)
-f参数将加速它(from man ls):
-f do not sort, enable -aU, disable -lst
Run Code Online (Sandbox Code Playgroud)
使用File.list()而不是File.listFiles()- String它返回的File对象比对象消耗更少的内存,并且(更重要的是,取决于目录的位置)它们不包含完整的路径名.
然后,File在处理结果时根据需要构造对象.
但是,这对于任意大的目录也不起作用.在目录层次结构中组织文件是一个总体上更好的想法,这样任何单个目录都不会有超过几千个条目.
如果您可以使用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;
}
});
Run Code Online (Sandbox Code Playgroud)