在java中读取一个巨大的Zip文件 - Out of Memory Error

时间:2011-12-28 07:55:06

标签: java zip

我正在使用java阅读ZIP文件,如下所示:

Enumeration<? extends ZipEntry> zes=zip.entries();
    while(zes.hasMoreElements()) {
        ZipEntry ze=zes.nextElement();
        // do stuff..
    }

我收到内存不足错误,zip文件大小约为160MB。堆栈跟踪如下:

Exception in thread "Timer-0" java.lang.OutOfMemoryError: Java heap space
at java.util.zip.InflaterInputStream.<init>(InflaterInputStream.java:88)
at java.util.zip.ZipFile$1.<init>(ZipFile.java:229)
at java.util.zip.ZipFile.getInputStream(ZipFile.java:229)
at java.util.zip.ZipFile.getInputStream(ZipFile.java:197)
at com.aesthete.csmart.batches.batchproc.DatToInsertDBBatch.zipFilePass2(DatToInsertDBBatch.java:250)
at com.aesthete.csmart.batches.batchproc.DatToInsertDBBatch.processCompany(DatToInsertDBBatch.java:206)
at com.aesthete.csmart.batches.batchproc.DatToInsertDBBatch.run(DatToInsertDBBatch.java:114)
at java.util.TimerThread.mainLoop(Timer.java:534)
at java.util.TimerThread.run(Timer.java:484)

如何在不增加堆大小的情况下枚举大型zip文件的内容?此外,当我不枚举内容并只访问这样的单个文件时:

ZipFile zip=new ZipFile(zipFile);
ZipEntry ze=zip.getEntry("docxml.xml");

然后我没有得到内存不足的错误。为什么会这样? Zip文件如何处理zip条目?另一种选择是使用ZIPInputStream。这会占用很少的内存吗?我需要最终在Amazon云(613 MB RAM)上的微EC2实例上运行此代码

编辑:提供有关我获取后如何处理zip条目的更多信息

Enumeration<? extends ZipEntry> zes=zip.entries();
    while(zes.hasMoreElements()) {
        ZipEntry ze=zes.nextElement();
        S3Object s3Object=new S3Object(bkp.getCompanyFolder()+map.get(ze.getName()).getRelativeLoc());
            s3Object.setDataInputStream(zip.getInputStream(ze));
            s3Object.setStorageClass(S3Object.STORAGE_CLASS_REDUCED_REDUNDANCY);
            s3Object.addMetadata("x-amz-server-side-encryption", "AES256");
            s3Object.setContentType(Mimetypes.getInstance().getMimetype(s3Object.getKey()));
            s3Object.setContentDisposition("attachment; filename="+FilenameUtils.getName(s3Object.getKey()));
            s3objs.add(s3Object);
    }

我从zipentry获取zipinputstream并将其存储在S3object中。我收集列表中的所有S3Objects,然后最终将它们上传到Amazon S3。对于那些不了解Amazon S3的人来说,它是一个文件存储服务。您通过HTTP上传文件。

我想也许是因为我收集了所有正在发生的个人输入流?如果我把它批量化它会有帮助吗?像一次100输入流?或者,如果我先解压缩然后使用解压缩的文件上传而不是存储流,会不会更好?

4 个答案:

答案 0 :(得分:2)

由于处理ZIP文件而导致内存不足异常非常不可思议。 Java类ZipFileZipEntry不包含任何可能填满613 MB内存的内容。

可能耗尽内存的是将ZIP存档的解压缩文件保留在内存中,或者更糟糕的是 - 将它们保存为XML DOM,这是一个非常耗费内存的。

切换到另一个ZIP库几乎没有帮助。相反,您应该考虑更改代码,以便它处理ZIP存档和包含的文件(如流),并且每次只将每个文件的有限部分保留在内存中。

BTW:如果你能提供关于巨大的 ZIP文件(它们包含许多小文件或几个大文件吗?)以及你对每个ZIP条目的处理方式的更多信息,我会很高兴。

<强>更新

感谢您提供更多信息。看起来你将ZIP文件的内容保存在内存中(尽管它在某种程度上取决于S3Object类的实现,我不知道)。

最好按照自己的建议实施某种批处理。例如,您可以将每个ZIP条目的解压缩大小相加,并在每次总大小超过100 MB时上传文件。

答案 1 :(得分:1)

我现在正在使用ZipFile类,正如我所看到的那样。可能使用ZipInputStream将是一个更好的选择,因为它有'closeEntry()'方法(我希望)解除分配条目使用的内存资源。但我之前没有使用它,这只是猜测。

答案 2 :(得分:0)

JVM的默认大小为64MB。 您需要在命令行上指定更大的大小。使用-Xmx开关。例如。 -Xmx256m

答案 3 :(得分:0)

实际上,java.util.zip.ZipFile有一个size()方法,但没有提供按索引访问条目的方法。也许您需要使用不同的ZIP库。我记得,我使用了TrueZIP相当大的档案。