一、从数据库中取大量数据(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分钟以上。