`
frank1998819
  • 浏览: 732018 次
  • 性别: Icon_minigender_1
  • 来自: 南京
文章分类
社区版块
存档分类

大量数据生成excel时候造成jvm内存泄漏问题的解决与测 (转)

    博客分类:
  • Java
 
阅读更多
一、从数据库中取大量数据(10万行左右)的时候,用jxl工具写excel,由于jxl是将每一个单元格生都成一个Cell对象,每一个对象都要消耗一定的内存空间,所以很容易导致内存溢出:

sheet0.addCell(new Label(colnum++,rownum,rs.getString("aname"),stuformat))//

tomcat报异常为:

java.lang.OutOfMemoryError: Java heap space

虽然在数据量在1—2万行左右的情况下,不会报异常,但是对服务器的资源消耗也是比较大的。

二、解决方法



要从消耗内存的原因出发解决,既然是由于生成大量的对象导致,就避免java创建太多的对象,就不能用jxl了。

另外可以对提前的数据进行情况来提取,对于不需要的数据可以不提取,节省时间和资源消耗。

用数据流的方法解决:

(1)直接写.txt文本文件,用”/t”来分割内容,可以从txt中直接复制到excel文件中。

public static void main(String[] args) {

       try {

           FileOutputStream fos = new FileOutputStream("hellotxt.txt");

           OutputStreamWriter osw=new OutputStreamWriter(fos);

           osw.write("aaa,bbb,ccc,ddd,eee,fff/r/n");

           osw.write("aaa/tbbb/tccc/tddd/teee/tfff/r/n");

           osw.flush();

           osw.close();

       } catch (IOException e) {

           e.printStackTrace();

       }

}

记事本打开效果如下:

aaa,bbb,ccc,ddd,eee,fff

aaa bbb ccc ddd eee fff

直接从记事本负责粘贴到excel中,效果如下:




aaa,bbb,ccc,ddd,eee,fff



















aaa


bbb


ccc


ddd


eee


fff




(2)也可以写成.csv

.csv文件打开格式跟excel基本一样,但是写入方式和写文本方式类似,可以以流的形式追加,只要在每列之间以固定标识符进行间隔,不存在内存和格式问题.

public static void main(String[] args) {

       try {

           FileWriter fw = new FileWriter("helloCsv.csv");

           fw.write("aaa,bbb,ccc,ddd,eee,fff,ggg,hhh/r/n");

           fw.write("aa1,bb1,cc1,dd1,ee1,ff1,gg1,hh1/r/n");

           fw.write("aaa/r/n");

           fw.write("aa2,bb2,cc2,dd2,ee2,ff2,gg2,hh2/r/n");

           fw.close();

       } catch (IOException e) {

           e.printStackTrace();

       }

    }



用excel打开文件显示如下,可以看到英文的逗号被作为了分割符而不显示出来。




aaa


bbb


ccc


ddd


eee


fff


ggg


hhh




aa1


bb1


cc1


dd1


ee1


ff1


gg1


hh1




aaa

























aa2


bb2


cc2


dd2


ee2


ff2


gg2


hh2


用记事本打开显示如下,逗号被作为内容的一部分显示出来:

aaa,bbb,ccc,ddd,eee,fff,ggg,hhh

aa1,bb1,cc1,dd1,ee1,ff1,gg1,hh1

aaa

aa2,bb2,cc2,dd2,ee2,ff2,gg2,hh2



注:写.csv文件的时候要注意里面的英文逗号,在用excel打开.csv文件的时候,英文逗号作为单元格之间的分隔符不显示出来。所以写的内容中的英文逗号要替换掉。



(3)写html

HTML   和   Excel之间通过改后缀名是可以相互转换的,  所以,可以先写HTML,这样内存不会溢出,写好后再改成xls后缀名。  

打开一个EXCEL,然后,选择另存为网页,可以看一下这个HTML的源码,直接把这个HTML文件后缀名改为xls,打开后效果和刚才那个EXCEL一样,但是会文件会变大。但是,压缩后比EXCEL还小。



以上几种方法以写.txt文件操作简单,效率比较高,写.csv文件效率跟写txt文件查不多,而且在小于65535行的情况下可以直接用excel打开,如果采用这个方法并且想用excel直接打开,行数如果多于65535行需要写多个.csv文件,并且要注意替换掉内容中的英文逗号,写html文件要写大量的标签,相对txt和csv文件内容会增加很多,具体写法可以上网查。比较优劣效率易编写情况,综合得出可以采用写txt和csv方法。





在网上查询相关资料,结合本案例,做的一个测试:

在java写文件中,通常会使用FileOutputStream和FileWriter,FileWriter只能写文本文件。 FileOutputStream也经常结合BufferedOutputStream。因为实际应用中写文本文件的情况占了大多数,对于java些excel的时候有jxl包,所以下面测试用不同的方式生成一个相同行数、大小相同的文件的四种不同方式。测试一下生成数据的时间,文件的大小,已经可以间接的看出消耗内存的情况。
   FileOutputStream 用于写入诸如图像数据之类的原始字节的流。

要写入字符流,请考虑使用 FileWriter。

BufferedOutputStream该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入基础输出流中,而不必为每次字节写入调用基础系统。
import java.io.BufferedOutputStream;

import java.io.File;

import java.io.FileOutputStream;

import java.io.FileWriter;



import jxl.Workbook;

import jxl.write.Label;

import jxl.write.WritableSheet;

import jxl.write.WritableWorkbook;



public class createDataFile {



  

    public static void main(String[] args) {

       // TODO Auto-generated method stub

       FileOutputStream out = null;

        FileOutputStream outSTr = null;

        BufferedOutputStream Buff=null;

        FileWriter fw = null;

        WritableWorkbook wb=null;

        int count=100000;//写文件行数

        try {

      

            long begin = System.currentTimeMillis();

            out = new FileOutputStream(new File("C:/add1.txt"));

            for (int i = 0; i < count; i++) {

                out.write("测试java 文件操作/r/n".getBytes());

            }

            out.close();

            long end = System.currentTimeMillis();

           // System.out.println((float)(new File("C:/add1.txt").length())/1024/1024);

            System.out.println("FileOutputStream执行耗时:" + (end - begin) + " 豪秒");

          

            long begin0 = System.currentTimeMillis();  

            outSTr = new FileOutputStream(new File("C:/add2.txt"));

            Buff=new BufferedOutputStream(outSTr);

            for (int i = 0; i < count; i++) {

                Buff.write("测试java 文件操作/r/n".getBytes());

            }

            Buff.flush();

            Buff.close();

            long end0 = System.currentTimeMillis();

            System.out.println("BufferedOutputStream执行耗时:" + (end0 - begin0) + " 豪秒");



          

            long begin3 = System.currentTimeMillis();

            fw = new FileWriter("C:/add3.txt");

            for (int i = 0; i < count; i++) {

                fw.write("测试java 文件操作/r/n");

            }

                fw.close();

            long end3 = System.currentTimeMillis();

            System.out.println("FileWriter执行耗时:" + (end3 - begin3) + " 豪秒");

            //生成excel

          

         

            long begin4=System.currentTimeMillis();

            wb=Workbook.createWorkbook( new  File( "c:/test.xls" ));         

            WritableSheet sheet0= wb.createSheet( "sheet1",0);

            //System.out.print(wb.getNumberOfSheets());

            int rownum=0;

            int limint=1;

            int snum=0;

            for(int i=0;i<count;i++){

              sheet0.addCell(new Label(0,rownum++,"测试java 文件操作"));  

              limint++;

              if(limint>65530){

               snum++;

                 wb.createSheet("sheet"+(wb.getNumberOfSheets()+1),wb.getNumberOfSheets()+1);//增加一个sheet

                 sheet0 = wb.getSheet(snum);

                 rownum  = sheet0.getRows();

                 limint=0;

              }

            }

            wb.write();

            wb.close();

            long end4=System.currentTimeMillis();

            System.out.println("jxl生成excel执行耗时:" + (end4 - begin4) + " 豪秒");

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            try {

            } catch (Exception e) {

                e.printStackTrace();

            }

        }

    }

}

以下结果经过多次执行,取常出现的数据,由于只是简单比较,不做详细统计。

1.    当count=1000的,即写文件1000行的时候,写出的文件大小为18.5KB:



FileOutputStream执行耗时:16 豪秒

BufferedOutputStream执行耗时:16 豪秒

FileWriter执行耗时:15 豪秒

jxl生成excel执行耗时:296 豪秒                 文件大小为47KB



2.当count=10000的,即写文件10000行的时候,写出的文件大小为185KB:



FileOutputStream执行耗时:94 豪秒

BufferedOutputStream执行耗时:15 豪秒

FileWriter执行耗时:16 豪秒

jxl生成excel执行耗时:391 豪秒                 文件大小为369KB

3.当count=100000的,即写文件100000行的时候,写出的文件大小为1856KB:



FileOutputStream执行耗时:594 豪秒

BufferedOutputStream执行耗时:94 豪秒

FileWriter执行耗时:78 豪秒

jxl生成excel执行耗时:2625 豪秒               文件大小为3592KB

4.当count=1000000的,即写文件1000000行的时候,写出的文件大小为18555KB:



FileOutputStream执行耗时:5625 豪秒

BufferedOutputStream执行耗时:1328 豪秒

FileWriter执行耗时:875 豪秒

执行生成excel的时候出现了异常:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space   



由以上数据可以看到,用文件流来写的速度比写excel的速度要快很多,生成的文件大小也比较小大小相差一半左右,而且从最后一项来看,写excel的时候对java虚拟机内存消耗也比较大,会报异常。

用流来写的三种方法中比较得出:如果不用缓冲流BufferedOutputStream,FileOutputStream写文件的是很不好的。当写 1000000行的文件的时候,FileOutputStream比FileWriter要慢4750毫秒, BufferedOutputStream比FileWriter慢553毫秒。
     不要小看这几秒的时间。当操作的数据量很大的时候,这点性能的差距就会很大了。在通用数据迁移工具导出数据库2千万条记录生成sql脚本文件的时候,性能性能相差2分钟以上。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics