尝试使用Apache POI写入Excel文件导致OutOfMemoryError错误

5
我有一个编写Excel文件的程序。它使用Apache POI来编写Excel 2007文件(我有超过256列,所以必须使用它)。程序可以运行。我已经在非常小的文件上测试过了,但如果我使用更多的行,就会用完内存。
以下是堆栈跟踪:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.io.ByteArrayOutputStream.write(Unknown Source)
    at org.apache.poi.openxml4j.opc.internal.MemoryPackagePartOutputStream.write(MemoryPackagePartOutputStream.java:88)
    at org.apache.xmlbeans.impl.store.Cursor._save(Cursor.java:590)
    at org.apache.xmlbeans.impl.store.Cursor.save(Cursor.java:2544)
    at org.apache.xmlbeans.impl.values.XmlObjectBase.save(XmlObjectBase.java:212)
    at org.apache.poi.xssf.usermodel.XSSFSheet.write(XSSFSheet.java:2480)
    at org.apache.poi.xssf.usermodel.XSSFSheet.commit(XSSFSheet.java:2439)
    at org.apache.poi.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:196)
    at org.apache.poi.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:200)
    at org.apache.poi.POIXMLDocument.write(POIXMLDocument.java:204)
    at model.Conversione.traduzioneFile(Conversione.java:219)
    at model.Main.scriviFile(Main.java:75)
    at model.Main.main(Main.java:51)

根据堆栈跟踪,错误发生在我写“workbook.write(fileOut)”的那一行,其中fileOut是FileOutputStream。这意味着显然有足够的内存来存储Excel文件的所有Java对象,但由于它在写入硬盘时必须获取更多内存,所以出现问题。
只是告诉你,我已经尝试增加Java堆大小,甚至达到1G(通过添加-Xms128m -Xmx1024m),但仍然无济于事。
救命啊!O.o
代码示例:
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

//I'M USING A DATABASE 
import DAO.EventoDAO;
import DAO.ParametroDAO;

public class Conversion {

public static void traduzioneFile(File read, File write){
    FileOutputStream fos=null;


    try {
        fos = new FileOutputStream(write);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    if (fos!=null) {

        try{

            Workbook wb = new XSSFWorkbook() ;

            Sheet sheet = wb.createSheet();

            //I'm reading from a table in a .txt file , converting values, and putting them in a table..

            FileInputStream fis;
            try {
                fis = new FileInputStream(fileLettura);
                InputStreamReader isr=new InputStreamReader(fis);
                BufferedReader br=new BufferedReader(isr);
                String line=br.readLine();

                //here there are some variables
                while(line!=null) {

                    Row row = null;
                    row=sheet.createRow((short)row_number);


                                            //arrayLinea contains all the words of the line
                    while (column_number<arrayLinea.length){
                    value=arrayLinea[column_number];
 //if value is ok i translate it and put it in a cell
                       row.createCell((short)contatoreColonne).setCellValue(value);
                                    contatoreColonne++                                  

                        }
                        //next line
                        linea=br.readLine();
                        row_line++;

                }



        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }catch (Exception ex){
            ex.printStackTrace();

        }


        wb.write(fos);
        fos.flush();
        fos.close();

    }catch (FileNotFoundException e){
    }catch (IOException e){
    }catch (Exception e){

    }

}
}

希望这篇文章能够易读... 但是我正在逐行扫描,逐列翻译数值,将它们放入单元格中... 这部分没问题... 我用 systems.out.println 测试过了 ^^ 但是在最后一行写着“翻译完成,开始写入”的时候,出现了错误...


请展示导致此问题的代码。 - Woot4Moo
好的,我正在编辑帖子,展示一个比真实代码简单的版本,因为它是相当复杂的工作的一部分:D - Lucia Belardinelli
你尝试过在任务管理器中检查jvm的内存使用情况吗?你尝试过设置newGC(请参阅java.sun.com/docs/hotspot/gc1.4.2/faq.html或acevedoalberto.wordpress.com/2009/01/16/jvmtuning)吗?你还可以尝试使用MAT来检查现有堆中是否存在内存泄漏,它可以帮助你很多。 - gaffcz
你使用的是哪个版本的Apache POI?如果不是POI 3.8 beta 4(或最近的夜间构建版本),那么切换到该版本是否有帮助? - Gagravarr
你看过在SO上类似问题的这个答案吗?https://dev59.com/gG445IYBdhLWcg3wXZIy#5038492 - Wivani
3个回答

14

使用POI编写 .xlsx 文件会消耗大量内存,1GB 可能不足以支持此操作。

最近 Apache POI 推出了一个新的 API(SXSSF),它是一种流式实现,用于编写 .xlsx 文件。我还没有亲自使用过,但也许这是你可以尝试的东西。


2
我刚刚尝试了一下 - 用SXSSFWorkbook()替换XSSFWorkbook(),一切都立刻完美地运行起来,而且非常非常快。谢谢! - Kimberley Coburn
1
请注意自动计算列大小的限制:https://stackoverflow.com/a/49514411/458157 @Turismo 谢谢 :) - gouessej

1

尝试增加堆空间。

如果您正在使用 Eclipse,则右键单击项目文件夹,选择“Run as” ->“Run Configuration” ->“Arguments”选项卡。

在“Arguments”选项卡中,尝试设置以下参数:

-Xms 设置Java堆的初始大小

-Xmx 设置Java堆的最大大小

-Xss 设置Java线程堆栈大小

示例:

-Xms1024M -Xmx1524M

然后运行程序。

我希望这样做可以解决问题。

对于回复晚了很抱歉 :D


0

你应该设置一个调试断点并跟踪你的代码。

我相信某个地方存在一个无限循环,导致了OOM错误。


没有问题的是编码部分,我已经进行了多次测试。但是如果文件包含超过特定行数(我一会儿会告诉你具体数字,等我找到它哈哈),异常会出现在wb.write(fos)这一行代码。 - Lucia Belardinelli
嗯...我无法修改列大小...每个我翻译的文件都有一些我不知道的列,但我必须处理它显示的每一列... - Lucia Belardinelli
Excel 2003可以创建,但使用XSSF可以创建Excel 2007和2010文件^^ - Lucia Belardinelli

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