大量数据生成excel时候造成jvm内存泄漏问题的解决与测
?
一、從數據庫中取大量數據(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分鐘以上。
總結
以上是生活随笔為你收集整理的大量数据生成excel时候造成jvm内存泄漏问题的解决与测的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 日期 运算
- 下一篇: 教你如何配置Tomcat 绑定指定目录的