2017-01-24 129 views
0

我在使用Apache POI處理.xlsx文件時遇到問題。我已閱讀了StackOverflow上的許多線程,以及Intellij和Oracle網站上的支持,並嘗試實施推薦的修補程序。每當JVM嘗試處理大於5 MB的Excel文件時,我仍然會發現內存不足錯誤。作爲參考,我使用的是8 GB RAM的iMac,並且我已經加強了JVM RAM分配,使其達到當前4 GB(一次512 MB)的水平,但沒有運氣。Apache POI OutOfMemoryError

我正在構建的程序處理目錄中的所有Excel電子表格,並將字段中的唯一值添加到HashSet中。所有電子表格處理完成後,生成的HashSet將被寫入文件。

無論何時遇到大於5 MB的文件,無論是第一個文件處理還是最後一個文件處理,GC都無法跟上,並且出現內存不足異常。 5 MB似乎是我成功讀入和處理excel文件的限制。對我來說這似乎很奇怪,一個剛剛超過5 MB的文件將會嚴重影響系統的資源,所以我想知道如果問題可能出現在我的代碼中?下面的主要方法。思考?

public class Launcher { 
     public static void main(String[] args) { 

      WVDataFileReader reader = new WVDataFileReader(); 
      HashSet<String> operators = reader.getOperatorsFromExcel("data/WV/production", 2); 
      FileOutput.writeToFile(operators, "/db/mysql/mysql-files/operators"); 
     } 
    } 


public abstract class RegulatoryDataFileReader { 

    private final String EXCEL_EXTENSION = "xlsx"; 
    protected static final Logger LOGGER = Logger.getLogger(RegulatoryDataFileReader.class.getName()); 


    protected abstract HashSet<String> processSheetForOperators(Sheet sheet, int firstDataRow, HashSet<String> set); 

    public HashSet<String> getOperatorsFromExcel(String directory, int firstDataRow) { 

     HashSet<String> temp = new HashSet<>(); 
     ArrayList<File> spreadsheets = getExcelFiles(directory); 
     Collections.sort(spreadsheets); 

     for (File excelFile : spreadsheets) { 
      System.out.println("Reading data from " + excelFile.getName()); 

      try { 
       Workbook workbook = WorkbookFactory.create(excelFile); 
       Sheet sheet = workbook.getSheetAt(0); // Assumes spreadsheet has 1 sheet 

       processSheetForOperators(sheet, firstDataRow, temp); 

       workbook.close(); 
      } catch (FileNotFoundException e) { 
       LOGGER.log(Level.SEVERE, e.toString(), e); 
      } catch (IOException e) { 
       LOGGER.log(Level.SEVERE, e.toString(), e); 
      } catch (InvalidFormatException e) { 
       LOGGER.log(Level.SEVERE, e.toString(), e); 
      } 
     } 
     return temp; 
    } 



    public class WVDataFileReader extends RegulatoryDataFileReader { 

     @Override 
     public HashSet<String> processSheetForOperators(Sheet sheet, int firstDataRow, HashSet<String> set) { 
      Iterator<Row> rowIterator = sheet.iterator(); 

      if (rowIterator.hasNext()) { 

       // Skip to the first row containing data 
       for (int i = 1; i < firstDataRow; i++) { 
        rowIterator.next(); 
       } 

       while (rowIterator.hasNext()) { 
        int columnNum = 0; 
        Row row = rowIterator.next(); // Advance row 
        Iterator<Cell> cellIterator = row.cellIterator(); 

        while (cellIterator.hasNext()) { 
         columnNum++; 
         Cell cell = cellIterator.next(); // Advance cell 

         switch (columnNum) { 
          case 4: 
           cell.setCellType(Cell.CELL_TYPE_STRING); 
           String operator = cell.getStringCellValue(); 
           operator = StrUtils.cleanString(operator);; 
           set.add(operator); 
           break; 
          default: 
           break; 
         } 
        } 
       } 
      } 
      return set; 
     } 

    } 
+1

POI處理usermodel API *中的'xlsx'文件的方式非常低效。不像'xls',它是用poi特定的代碼處理的,'xlsx'文件是用一些通用的xml處理的,oo庫將每個數據項都加入到一個對象中,封裝了一個昂貴的DOM樹。如果您只想迭代內容,您可以查看POI的流API,[本頁]末尾的表(https://poi.apache.org/spreadsheet/)比較了這些方法。 – Holger

+0

感謝您的信息!我無法相信用戶模式可能效率低下 - 這很荒謬。我的xlsx文件有大約50,000行38個數字列+ 1個字符串列(平均)包含大約20個字符。就原始數據而言,單元數據本身應該自己消耗略少於20MB的內存。對我來說這似乎很奇怪,Apache POI將需要幾GB的開銷來包裝這麼少量的數據,這就是爲什麼我認爲這個錯誤可能在我身上。 – Coop

回答

0

如果您只是閱讀xlsx文件,我會嘗試使用此庫來降低Apache POI使用的內存。 https://github.com/monitorjbl/excel-streaming-reader

請注意,並非所有的Apache POI API的方法都已實現,因此它可能無法解決您的特定用例。

問題是,Apache POI使用大量內存,並且根據Excel中的行數和列數(即使它們爲空),使用率似乎會增加。在我的情況下,我收到了只有2MB的Excel文檔的內存不足錯誤。