poi內存溢出解決辦法

前言

java操作Excel常用的兩種方式,分別爲:jxl和poi。今天記錄一下我在使用poi寫Excel時遇到的內存溢出問題,以及poi是如何提供的解決方法。
先附上各版本poi官網下載鏈接https://archive.apache.org/dist/poi/release/bin/

具體實現

  1. poi提供了兩種創建Excel的類:
    一種是2003版本的HSSF(文件擴展名爲xls),一張sheet表允許存2^16=65536次方行數據,2^8 = 256列數據;
    另一種是2007版本的XSSH(文件擴展名爲xlsx),一張sheet表允許存2^20 = 1048576行,2^14 = 16384列數據。

  2. 我在使用XSSH創建一張包含18萬數據的sheet時,程序報錯內存溢出,爲解決這個問題找到官方解決辦法,poi爲解決內存溢出在3.8之後的版本(不包含3.8)中添加一個新類SXSSF,它是2007版本的一個升級,通過限定內存中到達一定行數清空內存的方式解決內存溢出。但這個類只能寫,不能讀。

  3. 具體場景是從FTP上讀取一個txt文件,裏面包含18萬行的數據,需要讀取後保存到Excel中,代碼如下

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

public class Test {

    public static void main(String[] args) throws IOException {
         try{
             long curr_time=System.currentTimeMillis();

             FTPClient ftp = new FTPClient();
             int reply;
             ftp.connect("ip地址", 端口);
             ftp.setControlEncoding("GBK");
             ftp.login("用戶名","密碼");// 登錄
             ftp.setFileType(FTPClient.BINARY_FILE_TYPE);// 採用二進制上傳
             reply = ftp.getReplyCode();
             if (!FTPReply.isPositiveCompletion(reply)) {
                ftp.disconnect();
             }
            // 設置PassiveMode被動模式-向服務發送傳輸請求  
             ftp.enterLocalPassiveMode();  

             // 設置以二進制流的方式傳輸  
             ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
             ftp.changeWorkingDirectory("/student");//轉移到FTP服務器目錄

             int rowaccess=100;//內存中緩存記錄行數

             SXSSFWorkbook wb = new SXSSFWorkbook(rowaccess); //創建的workbook類是SXSSF
             Sheet sh = wb.createSheet();
             Row row = sh.createRow(0);

             Cell cell = row.createCell(0);
             cell.setCellValue("學號");

             cell = row.createCell(1);
             cell.setCellValue("姓名");

             cell = row.createCell(2);
             cell.setCellValue("性別");

             cell = row.createCell(3);
             cell.setCellValue("年齡");

             cell = row.createCell(4);
             cell.setCellValue("出生日期");

             InputStream ins = null;  
             String fileName = "student_2017.txt";
             ins = ftp.retrieveFileStream(fileName); // 從服務器上讀取指定的文件     
             BufferedReader reader = new BufferedReader(new InputStreamReader(ins, "GBK"));   
             String line = null;   
             int j = 0;
             while ((line = reader.readLine()) != null) {   
                    List<String> list = Arrays.asList(line.toString().split("\\|"));
                    j++;
                    row = sh.createRow((int) j);
                    for (int i = 0; i < list.size(); i++) {
                        row.createCell(i).setCellValue(list.get(i));
                    }
                    if( j % rowaccess==0){//每當行數達到設置的值就刷新數據到硬盤,以清理內存
                         ((SXSSFSheet)sh).flushRows();//關鍵就在這
                    }
                }

                /*寫數據到文件中*/
                FileOutputStream os = new FileOutputStream("d:/student/student_2017.xlsx");    
                wb.write(os);
                os.close();

                ftp.logout();

                /*計算耗時*/
                System.out.println("耗時:"+(System.currentTimeMillis()-curr_time)/1000);
           } catch(Exception e) {
               e.printStackTrace();
           }
        }
}

代碼中的關鍵點在於首先創建的workbook是SXSSFWorkbook ,並在參數中指定了多少行時清空內存,其次在於寫一個if判斷,判斷是否到達了指定行數,如果到達指定的行數執行((SXSSFSheet)sh).flushRows()用以清空緩存。
4. 效率
可以看到上面代碼中緩存行數爲100,那麼多少行一清是最好的呢,我特意用這18萬數據做了如下測試:

緩存行數 —- 執行時間
10000 ——— 196s
1000 ———– 26s
100 ———— 10s
10 ————– 8s
1 ————— 9s

所有總結得出在100行時是效率最好的選擇。

說明

下圖是我用到的包。
用到的包

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章