起因
最近正在接触POI中的SXSSFWorkbook,要说起为什么会接触这个货,那就要说最近的一个项目了
这个项目需要将10w多条数据写入Excel,但是整个逻辑执行过程一直伴随着OOM
经过
于是我用Java VisuaalVM工具进行了一番查证,并在OOM的时候获取了dump文件,打开发现我了个擦,里面有两个货占据了大量的空间:
1、org.apache.xmlbeans.impl.store.Xobj$ElementXobj 占据空间425312843
2、org.apache.xmlbeans.impl.store.Xobj$AttrXobj 占据空间403268798
![]() | ![]() |
既然找到OOM的真凶那么久相分析一下这个到底是个啥
经过查阅资料发现原来是POI的坑,XSSFWorkbook在创建Excel、或者说写Excel的时候内存开销是很大的。
于是乎apache的POI项目推出了一个实现大数据量的流式版本XSSFWorkbook,也称之为SXSSFWorkbook,它允许了我们编写非常大的文件而不会耗尽内存,因为在任何时候只有行的可配置部分保存在内存中,例如合并区域,注释等。
下面我进行了一组测试来进一步阐述问题,当然HSSFWorkbook我就不说了,小娃娃一般
测试代码原理很简单,循环100w次插入100w条行数据,观察Excel生成情况
public static void test1(){
try {
long t1 = System.currentTimeMillis();
SXSSFWorkbook workbook = new SXSSFWorkbook();
workbook.createSheet("aaa");
SXSSFSheet aaa = workbook.getSheetAt(0);
for (int i=0;i<1000000;i++){
aaa.createRow(i);
aaa.getRow(i).createCell(0).setCellValue("aaaaaaaaaaaaaaaaaaaaaaa");
aaa.getRow(i).createCell(1).setCellValue("aaaaaaaaaaaaaaaaaaaaaaa");
aaa.getRow(i).createCell(2).setCellValue("aaaaaaaaaaaaaaaaaaaaaaa");
aaa.getRow(i).createCell(3).setCellValue("aaaaaaaaaaaaaaaaaaaaaaa");
aaa.getRow(i).createCell(4).setCellValue("aaaaaaaaaaaaaaaaaaaaaaa");
}
OutputStream outputStream = null;
// 打开目的输入流,不存在则会创建
outputStream = new FileOutputStream("J:\\out.xlsx");
workbook.write(outputStream);
outputStream.close();
long t2 = System.currentTimeMillis();
System.out.println("SXSSFWorkbook : 100w条数据写入Excel 消耗时间:"+ (t2-t1));
} catch (Exception e) {
e.printStackTrace();
}
}
public static void test2(){
try {
long t1 = System.currentTimeMillis();
XSSFWorkbook workbook = new XSSFWorkbook();
workbook.createSheet("aaa");
XSSFSheet aaa = workbook.getSheetAt(0);
for (int i=0;i<1000000;i++){
aaa.createRow(i);
aaa.getRow(i).createCell(0).setCellValue("aaaaaaaaaaaaaaaaaaaaaaa");
aaa.getRow(i).createCell(1).setCellValue("aaaaaaaaaaaaaaaaaaaaaaa");
aaa.getRow(i).createCell(2).setCellValue("aaaaaaaaaaaaaaaaaaaaaaa");
aaa.getRow(i).createCell(3).setCellValue("aaaaaaaaaaaaaaaaaaaaaaa");
aaa.getRow(i).createCell(4).setCellValue("aaaaaaaaaaaaaaaaaaaaaaa");
}
OutputStream outputStream = null;
// 打开目的输入流,不存在则会创建
outputStream = new FileOutputStream("J:\\out2.xlsx");
workbook.write(outputStream);
outputStream.close();
long t2 = System.currentTimeMillis();
System.out.println("XSSFWorkbook : 100w条数据写入Excel 消耗时间:"+ (t2-t1));
} catch (Exception e) {
e.printStackTrace();
}
}
测试结果:
emmmm 好吧 内存溢出 那我把数据量调小点
可以看到SXSSFWorkbook确实优化很多