Java的Apache-poi,内存泄漏与Excel文件

我需要为我的论文阅读(15000)excel文件。 我正在使用Apache poi来打开,稍后分析它们,但大约5000个文件后,我得到以下exception和stacktrace:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at org.apache.xmlbeans.impl.store.Cur$CurLoadContext.attr(Cur.java:3044) at org.apache.xmlbeans.impl.store.Cur$CurLoadContext.attr(Cur.java:3065) at org.apache.xmlbeans.impl.store.Locale$SaxHandler.startElement(Locale.java:3263) 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:4682) 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:3479) at org.apache.xmlbeans.impl.store.Locale.parseToXmlObject(Locale.java:1277) at org.apache.xmlbeans.impl.store.Locale.parseToXmlObject(Locale.java:1264) at org.apache.xmlbeans.impl.schema.SchemaTypeLoaderBase.parse(SchemaTypeLoaderBase.java:345) at org.apache.poi.POIXMLTypeLoader.parse(POIXMLTypeLoader.java:92) at org.openxmlformats.schemas.spreadsheetml.x2006.main.WorksheetDocument$Factory.parse(Unknown Source) at org.apache.poi.xssf.usermodel.XSSFSheet.read(XSSFSheet.java:173) at org.apache.poi.xssf.usermodel.XSSFSheet.onDocumentRead(XSSFSheet.java:165) at org.apache.poi.xssf.usermodel.XSSFWorkbook.parseSheet(XSSFWorkbook.java:417) at org.apache.poi.xssf.usermodel.XSSFWorkbook.onDocumentRead(XSSFWorkbook.java:382) at org.apache.poi.POIXMLDocument.load(POIXMLDocument.java:178) at org.apache.poi.xssf.usermodel.XSSFWorkbook.<init>(XSSFWorkbook.java:249) at org.apache.poi.xssf.usermodel.XSSFWorkbook.<init>(XSSFWorkbook.java:302) at de.spreadsheet_realtions.analysis.WorkbookAnalysis.analyze(WorkbookAnalysis.java:18) 

代码 (目前只是打开文件并closures文件):

 public static void main(String[] args) { start(); } public void start(){ File[] files = getAllFiles(Config.folder); ZipSecureFile.setMinInflateRatio(0.00); for(File f: files){ analyze(f); } } public void analyze(File file){ Workbook workbook = null; try { workbook = new XSSFWorkbook(file); //line 18 } catch (Exception e1) {e1.printStackTrace(); return;} // later would be here the code to analyze the workbook try { workbook.close(); } catch (Exception e) {e.printStackTrace();} } 

我也尝试了OPCPackage.open(文件),我得到了同样的结果。

我做错了什么,或者我能做些什么来解决这个问题? 谢谢你的帮助。


编辑:下面的代码相同。

 try (XSSFWorkbook workbook = new XSSFWorkbook(file)){ } catch (Exception e1) {e1.printStackTrace(); return;} 

通常,POI在内存中具有整个工作簿。 所以,一个大的工作簿需要一个不同的方法。

写入时 ,可以使用SXSSF,大多数调用是相同的,只是在内存中只有一定数量的行。

就你而言,你正在阅读 。 为此,您可以使用他们的“事件驱动”API。 这里的基本想法是,你不把工作簿当成一个巨大的对象。 相反,当它被读取的时候,它会被零碎地分解,并且可以像你想要的那样把自己的数据保存到自己的数据结构中。 或者,您可以在阅读时简单地处理它,而不是节省很多。

由于这是一个较低级别的API(由正在读取的数据的结构驱动),所以XLS有一种方法,而XLSX有不同的方法。 查看兴趣点“如何”页面 ,并find“XSSF和SAX(Event API)”部分。

该示例演示了如何在读入时检测每个单元格的值(您需要在库path上使用xercesImpl.jar)。

如果在第一个尝试块中出现exception,则返回,所以不会closures工作簿。

把这个closures放在一个finally块中。

 Workbook workbook = null; try { workbook = new XSSFWorkbook(file); //line 18 // later would be here the code to analyze the workbook } catch (Exception e1) { e1.printStackTrace(); return; } finally { if (workbook != null) workbook.close(); } 

或者,最好使用试用资源。

 try (XSSFWorkbook workbook = new XSSFWorkbook(file) { // later would be here the code to analyze } catch (Exception e1) { e1.printStackTrace(); } // No need for explicit close.