使用ZipFileSystem压缩一个巨大的文件夹会导致OutOfMemoryError

通过将zip文件视为文件系统, java.nio包有一种处理zip文件的漂亮方式。 这使我们能够像普通文件一样处理zip文件内容。 因此,只需使用Files.copy将所有文件复制到zip文件中即可实现压缩整个文件夹。 由于子文件夹也要复制,我们需要一个访问者:

  private static class CopyFileVisitor extends SimpleFileVisitor { private final Path targetPath; private Path sourcePath = null; public CopyFileVisitor(Path targetPath) { this.targetPath = targetPath; } @Override public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException { if (sourcePath == null) { sourcePath = dir; } else { Files.createDirectories(targetPath.resolve(sourcePath .relativize(dir).toString())); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { Files.copy(file, targetPath.resolve(sourcePath.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING); return FileVisitResult.CONTINUE; } } 

这是一个简单的“复制目录递归”访问者。 它用于递归复制目录。 但是,使用ZipFileSystem ,我们还可以使用它将目录复制到zip文件中,如下所示:

 public static void zipFolder(Path zipFile, Path sourceDir) throws ZipException, IOException { // Initialize the Zip Filesystem and get its root Map env = new HashMap(); env.put("create", "true"); URI uri = URI.create("jar:" + zipFile.toUri()); FileSystem fileSystem = FileSystems.newFileSystem(uri, env); Iterable roots = fileSystem.getRootDirectories(); Path root = roots.iterator().next(); // Simply copy the directory into the root of the zip file system Files.walkFileTree(sourceDir, new CopyFileVisitor(root)); } 

这就是我称之为压缩整个文件夹的优雅方式。 但是,当在一个巨大的文件夹(大约3 GB)上使用此方法时,我收到一个OutOfMemoryError (堆空间)。 使用通常的zip处理库时,不会引发此错误。 因此,似乎ZipFileSystem处理副本的方式效率非常低:要写入的文件太多都会保留在内存中,因此会出现OutOfMemoryError

为什么会这样? 使用ZipFileSystem通常被认为是低效的(在内存消耗方面)或我在这里做错了什么?

我查看了ZipFileSystem.java,我相信我找到了内存消耗的来源。 默认情况下,实现使用ByteArrayOutputStream作为压缩文件的缓冲区,这意味着它受分配给JVM的内存量的限制。

我们可以使用一个(未记录的)环境变量来使实现使用临时文件( "useTempFile" )。 它的工作原理如下:

 Map env = new HashMap<>(); env.put("create", "true"); env.put("useTempFile", Boolean.TRUE); 

更多细节在这里: http : //www.docjar.com/html/api/com/sun/nio/zipfs/ZipFileSystem.java.html ,有趣的行是96,1358和1362。

您必须准备jvm以允许使用-Xms {memory} -Xmx {memory}那些内存量。

我建议你查看目录计算磁盘空间并设置一个限制,在1Gb下使用内存文件系统,超过1GB使用磁盘文件系统。

另外,检查方法的并发性,你不会喜欢超过1个线程压缩3Gb的文件