Java Apache POI

4

我正在处理从一个Excel文档中读取数据的问题,该文档每两周更新一次,包含约50,000行数据,并且可能在开始新工作表之前达到约120,000行。 我使用Apache POI获取数据。 我遇到了以下异常,但我相信最重要的异常是java.lang.OutOfMemoryError:Java堆空间。 我已经在数据较少的另一个Excel工作表上测试过我的代码并成功读取了数据。

如果你有任何关于如何读取大型Excel文档的方法或建议,请分享。

org.apache.poi.POIXMLException: java.lang.reflect.InvocationTargetException
    at org.apache.poi.xssf.usermodel.XSSFFactory.createDocumentPart(XSSFFactory.java:62)
    at org.apache.poi.POIXMLDocumentPart.read(POIXMLDocumentPart.java:456)
    at org.apache.poi.POIXMLDocumentPart.read(POIXMLDocumentPart.java:461)
    at org.apache.poi.POIXMLDocumentPart.read(POIXMLDocumentPart.java:461)
    at org.apache.poi.POIXMLDocumentPart.read(POIXMLDocumentPart.java:461)
    at org.apache.poi.POIXMLDocument.load(POIXMLDocument.java:162)
    at org.apache.poi.xssf.usermodel.XSSFWorkbook.<init>(XSSFWorkbook.java:271)
    at excelreader.readExcelFile(excelreader.java:28)
    at excelreader.main(excelreader.java:18)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.apache.poi.xssf.usermodel.XSSFFactory.createDocumentPart(XSSFFactory.java:60)
    ... 13 more
Caused by: java.lang.OutOfMemoryError: Java heap space
    at org.apache.xmlbeans.impl.store.Cur.createElementXobj(Cur.java:260)
    at org.apache.xmlbeans.impl.store.Cur$CurLoadContext.startElement(Cur.java:2995)
    at org.apache.xmlbeans.impl.store.Locale$SaxHandler.startElement(Locale.java:3207)
    at org.apache.xmlbeans.impl.piccolo.xml.Piccolo.reportStartTag(Piccolo.java:1082)
    at org.apache.xmlbeans.impl.piccolo.xml.PiccoloLexer.parseAttributesNS(PiccoloLexer.java:1822)
    at org.apache.xmlbeans.impl.piccolo.xml.PiccoloLexer.parseOpenTagNS(PiccoloLexer.java:1521)
    at org.apache.xmlbeans.impl.piccolo.xml.PiccoloLexer.parseTagNS(PiccoloLexer.java:1362)
    at org.apache.xmlbeans.impl.piccolo.xml.PiccoloLexer.yylex(PiccoloLexer.java:4678)
    at org.apache.xmlbeans.impl.piccolo.xml.Piccolo.yylex(Piccolo.java:1290)
    at org.apache.xmlbeans.impl.piccolo.xml.Piccolo.yyparse(Piccolo.java:1400)
    at org.apache.xmlbeans.impl.piccolo.xml.Piccolo.parse(Piccolo.java:714)
    at org.apache.xmlbeans.impl.store.Locale$SaxLoader.load(Locale.java:3454)
    at org.apache.xmlbeans.impl.store.Locale.parseToXmlObject(Locale.java:1276)
    at org.apache.xmlbeans.impl.store.Locale.parseToXmlObject(Locale.java:1263)
    at org.apache.xmlbeans.impl.schema.SchemaTypeLoaderBase.parse(SchemaTypeLoaderBase.java:345)
    at org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPivotCacheRecords$Factory.parse(Unknown Source)
    at org.apache.poi.xssf.usermodel.XSSFPivotCacheRecords.readFrom(XSSFPivotCacheRecords.java:62)
    at org.apache.poi.xssf.usermodel.XSSFPivotCacheRecords.<init>(XSSFPivotCacheRecords.java:53)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.apache.poi.xssf.usermodel.XSSFFactory.createDocumentPart(XSSFFactory.java:60)
    at org.apache.poi.POIXMLDocumentPart.read(POIXMLDocumentPart.java:456)
    at org.apache.poi.POIXMLDocumentPart.read(POIXMLDocumentPart.java:461)
    at org.apache.poi.POIXMLDocumentPart.read(POIXMLDocumentPart.java:461)
    at org.apache.poi.POIXMLDocumentPart.read(POIXMLDocumentPart.java:461)
    at org.apache.poi.POIXMLDocument.load(POIXMLDocument.java:162)
    at org.apache.poi.xssf.usermodel.XSSFWorkbook.<init>(XSSFWorkbook.java:271)
    at excelreader.readExcelFile(excelreader.java:28)
    at excelreader.main(excelreader.java:18)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

3
你尝试过增加最大堆分配量吗? - Mena
是的,我使用的是32位Java。我将其提高到512M,但仍然遇到了相同的异常。 - Allee Clark
您可以在32位上将最大值设置为4GB。再微调一下? - Mena
@Mena,我认为这个问题是由于内存泄漏引起的。如果使用1GB堆大小无法解决,那么仅增加堆大小可能不会有太大帮助,因为……http://www.azulsystems.com/technology/java-heap-size和http://javarevisited.blogspot.in/2011/05/java-heap-space-memory-size-jvm.html - Viraj Nalawade
@VirajNalawade 可能存在内存泄漏,但帮助 OP 解决这个问题非常困难,因为他们需要扫描整个应用程序。我很怀疑 Apache XmlBeans 中存在内存泄漏,但你永远不知道 - 查看版本和错误修复可能会有所帮助。最简单的方法是将堆提高到更高的水平 - 毕竟我们在处理大型文档。 - Mena
1
@Mena 非常有帮助,感谢你提供了详细的解释。 - Viraj Nalawade
3个回答

2

1. 解决Java的OutOfMemoryError的简单方法是通过使用JVM选项"-Xmx512M"来增加最大堆大小,这将立即解决您的OutOfMemoryError。

Java虚拟机(JVM)运行时具有固定的上限内存限制,您可以通过修改它来改变:

-Xms<size> - Set initial Java heap size
-Xmx<size> - Set maximum Java heap size

$ java -Xms512m -Xmx1024m JavaApp

解决Java中的OutOfMemoryError错误的第二种方法比较困难,当你没有太多内存并且即使增加最大堆大小后仍然出现java.lang.OutOfMemoryError时,你可能想要对应用程序进行分析,并查找任何内存泄漏。

因此,为了检查内存泄漏,您可以使用一些工具。如果没有这些工具,这将非常困难。一些流行的工具如下:

Jmap是一个与JDK6一起提供的命令行实用程序,允许您将堆的内存转储到文件中。它很容易使用,如下所示:

jmap -dump:format=b,file=heapdump 6054

Eclipse memory analyzer (MAT)
是来自eclipse基金会的一款工具,用于分析Java堆转储。它有助于查找类加载器泄漏和内存泄漏,并帮助最小化内存消耗。您可以使用MAT分析携带数百万个对象的堆转储,它还可以帮助您提取内存泄漏的嫌疑对象。


谢谢,我马上试一下。 - Allee Clark

1

这与堆大小有关。我将其设置为2g,仍然出现内存不足错误。我正在处理的Excel文档是一个数据透视表,而Apache POI对此有限制。这导致了第一个异常。


尝试优化工作,例如及时使用引用并在对象对您无用时将其置空。 - Viraj Nalawade

0

尝试运行 java.exe -Xmx512m -jar yourjar.jar

这将把最大堆大小设置为512 MB。如果512不够,可以尝试更大的值。


我增加了空间,但仍然出现相同的错误。感谢您的输入。 - Allee Clark
@talex:感谢你的修改。(at)Allee:项目进展如何?应用程序运行时间更长了吗,或者有任何影响吗?尝试使用64位Java并设置-Xmx2048m。这个改变有什么效果吗? - chris
2
@AlleeClark 50000行将使用大量内存。尝试将最大值设置为(对于32位Java版本,它在1.5到2 GB之间)。 - talex
它正在以相同的速度编译。我认为这是因为我正在从一个数据透视表中读取。我尝试了2 GB,仍然收到了相同的异常。 - Allee Clark
好的,那么在进一步操作之前,我建议先减小数据大小。在此之后,您或许可以估算出什么是可能的,什么是不可能的。也许您需要选择另一种策略来解决问题。 - chris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接