2017-01-24 5 views
0

Apache POIで.xlsxファイルを処理中に問題が発生しています。私はStackOverflowのスレッドの多くを読んだだけでなく、IntellijとOracleのサイトでもサポートしています。推奨される修正を実装しようとしました。 JVMが5 MBを超えるファイルを処理しようとすると、メモリ不足エラーが発生します。参考までに、私は8GBのRAMを搭載したiMacを利用しており、JVMのRAM割り当てを現在の4GB(一度に512MB)のレベルまで上げました。Apache POI OutOfMemoryError

私が作成しているプログラムはすべて、ディレクトリ内のスプレッドシートを秀で、フィールドから一意の値をHashSetに追加します。結果のHashSetは、すべてのスプレッドシートが処理された後にファイルに書き込まれます。

ファイルが最初に処理されたファイルであるか最後に処理されたファイルであろうと、5 MBを超えるファイルが見つかると、GCは追い続けることができず、メモリ不足例外が発生します。エクセルファイルの読み込みと処理には5 MBの制限があるようです。奇妙に思えるのは、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'ファイルを処理する方法は、ひどく非効率的です。 poi固有のコードで扱われる 'xls'とは異なり、' xlsx'ファイルは、あらゆるデータ項目をオブジェクトに膨らませて高価なDOMツリーをカプセル化するライブラリにいくつかの一般的なxmlで処理されます。コンテンツのみを反復したい場合は、POIのストリーミングAPIを参照してください。[このページ](https://poi.apache.org/spreadsheet/)の最後の表は、アプローチを比較しています。 – Holger

+0

情報ありがとう!私はusermodelがそれが非効率的であるかもしれないと信じることができない - それはばかげている。私の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ドキュメントでメモリ不足が発生しました。