自己挖的坑自己填--jxl进行Excel下载堆内存溢出问题 源码仓库Gitee · GitHub今天在进行使用 jxl 进行 Excel 下载时由于数据量大4万多条接近5万条数据的下载数据结构过于负责存在大量大对象虽然在对象每次用完都设置为null但还是存在内存溢出问题加上本地电脑内存不大只有8G导致下载数据时报堆内存溢出下载失败。Exception data: java.lang.OutOfMemoryError at jxl.write.biff.MemoryDataOutput.write(MemoryDataOutput.java:72) at jxl.write.biff.File.write(File.java:149) at jxl.write.biff.RowRecord.writeCells(RowRecord.java:324) at jxl.write.biff.SheetWriter.write(SheetWriter.java:479) at jxl.write.biff.WritableSheetImpl.write(WritableSheetImpl.java:1431) at jxl.write.biff.WritableWorkbookImpl.write(WritableWorkbookImpl.java:915)下面是案例复现的简单模拟代码dependency groupIdnet.sourceforge.jexcelapi/groupId artifactIdjxl/artifactId version2.6.12/version /dependency public class MyExcel { public static void main(String[] args) throws Exception { File xlsFile new File(E:\\jxl.xls); // 创建一个工作簿 WritableWorkbook workbook Workbook.createWorkbook(xlsFile); // 创建一个工作表 WritableSheet sheet workbook.createSheet(sheet1, 0); MapInteger, ArrayListMapInteger, Person mapListMap new HashMap(); AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(MyBatisConfig.class); PersonServiceImpl bean context.getBean(PersonServiceImpl.class); //获取数据库数据 ListPerson listBean bean.getList(); //进行数据处理 for (int row 0; row 45000; row) { ArrayListMapInteger, Person listMap new ArrayListMapInteger, Person(); for (int col 0; col 30; col) { MapInteger, Person map new HashMapInteger, Person(); for (int j 0; j listBean.size(); j) { map.put(j, listBean.get(j)); } listMap.add(map); map null; } mapListMap.put(row, listMap); listMap null; } //写数据 for (int row 0; row 45000; row) { ArrayListMapInteger, Person listMap mapListMap.get(row); for (int col 0; col 30; col) { MapInteger, Person map listMap.get(col); for (int j 0; j listBean.size(); j) { Person person map.get(j); sheet.addCell(new Label(col, row, person.getId() row col)); sheet.addCell(new Label(col, row, person.getName() row col)); sheet.addCell(new Label(col, row, person.getAge() row col)); map null; } } listMap null; } workbook.write(); workbook.close(); }运行后结果解决办法1最简单的方法是加大内存本地电脑内存过小当把代码部署到公司测试环境测试环境内存是16G时该问题不再复现2采用临时文件写入EXCEL功能设定临时文件的位置可以有效的避免内存溢出jxl版本必须是2.6.12及其以上2.6版本没有采用临时文件的功能public static void main(String[] args) throws Exception { File xlsFile new File(E:\\jxl.xls); // 创建一个工作簿 WorkbookSettings ws new WorkbookSettings(); ws.setUseTemporaryFileDuringWrite(true); ws.setTemporaryFileDuringWriteDirectory(new File(E:\\));//指定一个临时文件路径 WritableWorkbook workbook Workbook.createWorkbook(xlsFile,ws); // 创建一个工作表 WritableSheet sheet workbook.createSheet(sheet1, 0); MapInteger, ArrayListMapInteger, Person mapListMap new HashMap(); AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(MyBatisConfig.class); PersonServiceImpl bean context.getBean(PersonServiceImpl.class); //获取数据库数据 ListPerson listBean bean.getList(); //进行数据处理 for (int row 0; row 45000; row) { ArrayListMapInteger, Person listMap new ArrayListMapInteger, Person(); for (int col 0; col 30; col) { MapInteger, Person map new HashMapInteger, Person(); for (int j 0; j listBean.size(); j) { map.put(j, listBean.get(j)); } listMap.add(map); map null; } mapListMap.put(row, listMap); listMap null; } //写数据 for (int row 0; row 45000; row) { ArrayListMapInteger, Person listMap mapListMap.get(row); for (int col 0; col 30; col) { MapInteger, Person map listMap.get(col); for (int j 0; j listBean.size(); j) { Person person map.get(j); sheet.addCell(new Label(col, row, person.getId() row col)); sheet.addCell(new Label(col, row, person.getName() row col)); sheet.addCell(new Label(col, row, person.getAge() row col)); } map null; } listMap null; } workbook.write(); workbook.close(); }当开始执行下载时会产生一个临时文件jxl 会把数据写入到这个临时文件中写入结束后会把这个临时文件删除经过 jconsole.exe 监控可以看出使用了临时文件后多了一个线程进行类的卸载使用了临时文件的线程高峰有15个类卸载了6个没有使用临时文件时线程数一直保持14个到报内存溢出结束0个类卸载PS1Excel 表一个 sheet 页可以存储6万条所以一般超过5万条的其他的数据就存储其他sheet中或采用分成几个 Excel 表来实现数据下载2今天测试下载的 Excel 数据文本有170M数据有4.8w条通过MS Office 打开失败为空 Excel 表但通过 WPS 可以正常打开。