java向excel 寫入海量資料記憶體溢位問題 解決

DN金猿發表於2020-11-11

由於專案中有匯出海量資料的需求,在谷歌和百度也沒有找到好的解決辦法,經過仔細研究發現poi-3.8版本以上提供新的模式可以滿足這個需求,寫在這裡希望能對有同樣需求的同行們有所幫助。

以下是測試程式碼:

import java.io.FileOutputStream;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.poi.ss.usermodel.Cell;
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.ss.util.CellReference;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

/**
 * @description:TestA
 * @author     :liuf 
 * @since      :2020/11/11 9:57
 */
public class TestA {

    /*** @param args*/
    public static void main(String[] args) {
        if (args[0].equals("hssf")) {
            hssfTest();
        }
        if (args[0].equals("sxssf")) {
            sxssfTest();
        }

    }

    public static void sxssfTest() {

        Workbook wb = new SXSSFWorkbook(100); // keep 100 rows in memory, exceeding rows will be flushed to disk
        Sheet sh = wb.createSheet();
        int rownum = 0;
        try {
            while (true) {
                Row row = sh.createRow(rownum);
                for (int cellnum = 0; cellnum < 10; cellnum++) {
                    Cell cell = row.createCell(cellnum);
                    String address = new CellReference(cell).formatAsString();
                    cell.setCellValue(address);
                }
                System.out.println(rownum);
                rownum++;
                if (rownum >= 1000000) break;
            }

            FileOutputStream out = new FileOutputStream("c:/sxssf.xlsx");
            wb.write(out);
            out.close();
        } catch (Exception e) {
            System.out.println(ExceptionUtils.getFullStackTrace(e));
        }
    }

    public static void hssfTest() {
        XSSFWorkbook wb = new XSSFWorkbook();
        Sheet sh = wb.createSheet();
        int rownum = 0;
        try {
            while (true) {
                Row row = sh.createRow(rownum);
                for (int cellnum = 0; cellnum < 10; cellnum++) {
                    Cell cell = row.createCell(cellnum);
                    String address = new CellReference(cell).formatAsString();
                    cell.setCellValue(address);
                }
                System.out.println(rownum);
                rownum++;
                if (rownum >= 1000000) break;
            }

            FileOutputStream out = new FileOutputStream("c:/hssf.xlsx");
            wb.write(out);
            out.close();
        } catch (Exception e) {
            System.out.println(ExceptionUtils.getFullStackTrace(e));
        }
    }
}

將工程打包成jar到C:,然後用命令列java -jar -Xms128m -Xmx512m -XX:PermSize=128M -XX:MaxPermSize=512M test.jar hssf 執行,在命令列視窗輸出到45000之後,輸出明顯減慢,

很快輸出如下異常:

5028850289502905029150292 Exception in thread “main”
        java.lang.reflect.InvocationTargetException 
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)
        at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)Caused by:java.lang.OutOfMemoryError:Java heap space

繼續用命令列java -jar -Xms128m -Xmx512m -XX:PermSize=128M -XX:MaxPermSize=512M test.jar sxssf執行,

命令列視窗輸出速度一直保持不變,直到迴圈完了,並最終生成四十多M的excel—sxssf.xlsx

相關文章