常用的excel生成工具包括jxl、poi。但二者都存在一个问题:生成excel需要大量的消耗内存。如果一次性往excel中写入的数据足够的多将导致内存溢出。
1、数据写入excel为什么会大量的消耗内存?
这需要从excel特点以及jxl和poi的实现原理来分析。excel即表格,一个一个的单元格。我们生成excel就是往一个个的单元格里面写数据。如下代码(使用的jxl):
WritableWorkbook wwb = Workbook.createWorkbook(new File(fileName)); WritableSheet ws = wwb.createSheet(wjmc, 0); WritableFont wfont = new jxl.write.WritableFont( WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false, UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.BLACK); jxl.write.WritableCellFormat normalStyle = new jxl.write.WritableCellFormat(wfont); normalStyle.setLocked(true);// 保护单元格 /**写入文件表头 begin */ Label labelC = null; labelC = new Label(0, 0, "零件名称"); ws.addCell(labelC); labelC = new Label(1, 0, "精友零件编码"); ws.addCell(labelC); StringBuffer sycxString=null; if (rs != null) { int j = 1; while (rs.next()) { String id = rs.getString("id"); sycxList = getSycxList(id); sycxString = new StringBuffer(); if (sycxList != null) { for (Iterator it = sycxList.iterator(); it.hasNext();) { sycxString.append((String) it.next()).append("\n"); } } String commonPart=rs.getString("sec_id") == null?"否":"是"; labelC = new Label(0, j, rs.getString("ljmc"), normalStyle); ws.addCell(labelC); labelC = new Label(1, j, rs.getString("ljbm"), normalStyle); ws.addCell(labelC); j++; }
这是一段典型的excel生成代码。这样的逻辑在数据量小的时候没有问题。但一旦数据量大,如红色标注的那一行:每一个单元格都需要new一个对象。最重要的,这些对象被excel对象WritableSheet所引用,在excel整个生成完成前无法被回收。内存就这样被占据消耗了!如果多用户同时并发导出excel.....显然jvm内存溢出是必然的!
2、如何避免内存溢出?
上面分析的内存溢出的主要原因是当数据量大的时候,开辟的内存空间一直被占据着不释放。如果能缩短被占据的时间,内存就能及时被释放。从而避免内存溢出!要缩短被占据的时间,就要减少写入一个excel中的数据量。让单个的excel短时间内就能完全生成。即将大量的数据分批次写入到多个excel中!这样当一个excel生成完成之后,它所引用的单元格对象就能被回收释放。
poi3.8版本之后引入了一种全新的解决此类内存溢出的方法。如下代码:
Workbook wb = new SXSSFWorkbook(100); // keep 100 rows in memory, exceeding rows will be flushed to disk Sheet sh = wb.createSheet(); for(int rownum = 0; rownum < 100000; rownum++){ 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); } } FileOutputStream out = new FileOutputStream("/temp/sxssf.xlsx"); wb.write(out); out.close();
在生成excel对象的时候可以指定其在内存中保留的数据量。超出指定数据量的数据后,会将之前的数据转到硬盘上。(注:此种方法是否有效个人尚未验证过)这样就避免了所有的单元格数据都保留在内存中,占据着不释放!