zoukankan      html  css  js  c++  java
  • 自己挖的坑自己填--jxl进行Excel下载堆内存溢出问题

      今天在进行使用 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>
                <groupId>net.sourceforge.jexcelapi</groupId >
                <artifactId>jxl</artifactId >
                <version>2.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);
            Map<Integer, ArrayList<Map<Integer, Person>>> mapListMap = new HashMap<>();
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyBatisConfig.class);
            PersonServiceImpl bean = context.getBean(PersonServiceImpl.class);
            //获取数据库数据
            List<Person> listBean = bean.getList();
            //进行数据处理
            for (int row = 0; row < 45000; row++) {
                ArrayList<Map<Integer, Person>> listMap = new ArrayList<Map<Integer, Person>>();
                for (int col = 0; col < 30; col++) {
                    Map<Integer, Person> map = new HashMap<Integer, 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++) {
                ArrayList<Map<Integer, Person>> listMap = mapListMap.get(row);
                for (int col = 0; col < 30; col++) {
                    Map<Integer, 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);
            Map<Integer, ArrayList<Map<Integer, Person>>> mapListMap = new HashMap<>();
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyBatisConfig.class);
            PersonServiceImpl bean = context.getBean(PersonServiceImpl.class);
            //获取数据库数据
            List<Person> listBean = bean.getList();
            //进行数据处理
            for (int row = 0; row < 45000; row++) {
                ArrayList<Map<Integer, Person>> listMap = new ArrayList<Map<Integer, Person>>();
                for (int col = 0; col < 30; col++) {
                    Map<Integer, Person> map = new HashMap<Integer, 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++) {
                ArrayList<Map<Integer, Person>> listMap = mapListMap.get(row);
                for (int col = 0; col < 30; col++) {
                    Map<Integer, 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个类卸载:

      PS:

      (1)Excel 表一个 sheet 页可以存储6万条,所以一般超过5万条的,其他的数据就存储其他sheet中;或采用分成几个 Excel 表来实现数据下载;

      (2)今天测试下载的 Excel 数据文本有170M(数据有4.8w条),通过MS Office 打开失败,为空 Excel 表,但通过 WPS 可以正常打开。

    作者:huangrenhui
    欢迎任何形式的转载,但请务必注明出处。
    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
    如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
    如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【码猿手】。
    限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
  • 相关阅读:
    设计模式之工厂模式-抽象工厂(02)
    1036 跟奥巴马一起编程 (15 分)
    1034 有理数四则运算 (20 分)
    1033 旧键盘打字 (20 分)
    1031 查验身份证 (15 分)
    大学排名定向爬虫
    1030 完美数列 (25 分)二分
    1029 旧键盘 (20 分)
    1028 人口普查 (20 分)
    1026 程序运行时间 (15 分)四舍五入
  • 原文地址:https://www.cnblogs.com/huangrenhui/p/14608463.html
Copyright © 2011-2022 走看看