【问题标题】:Apache POI XSSFWorkbook memory leakApache POI XSSFWorkbook 内存泄漏
【发布时间】:2018-04-29 05:24:05
【问题描述】:

所以我正在用 Java 制作一个大型素数生成器(在 JavaFX 的帮助下)。

它使用 Apache POI 库(我相信我使用的是 v3.17)将结果输出到 Excel 电子表格。

此导出逻辑的静态方法保存在名为 ExcelWriter 的类中。基本上,它遍历 Arraylist 参数并使用其内容填充 XSSFWorkbook。后记,一个 FileOutputStream 用于实际使其成为一个 excel 文件。以下是它的相关部分:

public class ExcelWriter {

//Configured JFileChooser to make alert before overwriting old files
private static JFileChooser fileManager = new JFileChooser(){
@Override
public void approveSelection(){
    ...
}        
};


private static FileFilter filter = new FileNameExtensionFilter("Excel files","xlsx");
private static boolean hasBeenInitialized = false;



//Only method that can be called externally to access this class's functionality
public static <T extends Object> void makeSpreadsheet
    (ArrayList<T> list,  spreadsheetTypes type, int max, String title, JFXProgressBar progressBar) 
            throws IOException, InterruptedException{
    progressBar.progressProperty().setValue(0);
    switch (type){
        case rightToLeftColumnLimit:
            makeSpreadsheetRightToLeft(list, false, max, title, progressBar);
            break;
       ...
    }
}


static private <T extends Object> void makeSpreadsheetRightToLeft
    (ArrayList<T> list,  boolean maxRows, int max, String title, JFXProgressBar progressBar) 
            throws IOException, InterruptedException{
    initializeChooser();
    XSSFWorkbook workbook = new XSSFWorkbook();
    XSSFSheet sheet = workbook.createSheet("Primus output"); 
    int rowPointer = 0;
    int columnPointer = 0;
    double progressIncrementValue = 1/(double)list.size();

    //Giving the spreadsheet an internal title also
    Row row = sheet.createRow(0);
    row.createCell(0).setCellValue(title);

    row = sheet.createRow(++rowPointer);

    //Making the sheet with a max column limit
    if (!maxRows){            
        for (T number: list){ 
            if (columnPointer == max){
                columnPointer = 0;
                row = sheet.createRow(++rowPointer);
            }
            Cell cell = row.createCell(columnPointer++);
            progressBar.setProgress(progressBar.getProgress() + progressIncrementValue);
            cell.setCellValue(number.toString());             
        }
    }else {
        //Making the sheet with a max row limit
        int columnWrapIndex = (int)Math.ceil(list.size()/(float)max);
        for (T number: list){ 
            if (columnPointer == columnWrapIndex){
                columnPointer = 0;
                row = sheet.createRow(++rowPointer);
            }
            Cell cell = row.createCell(columnPointer++);
            progressBar.setProgress(progressBar.getProgress() + progressIncrementValue);
            cell.setCellValue(number.toString());
        }         
    }
    writeToExcel(workbook, progressBar);


}


static private void writeToExcel(XSSFWorkbook book, JFXProgressBar progressBar) throws IOException, InterruptedException{
    //Exporting to Excel
    int returnValue = fileManager.showSaveDialog(null);

    if (returnValue == JFileChooser.APPROVE_OPTION){
        File file = fileManager.getSelectedFile();

        //Validation logic here


        try{

            FileOutputStream out = new FileOutputStream(file);
            book.write(out);
            out.close();
            book.close();
        }catch (FileNotFoundException ex){

        } 

    }
}
}

之后,我的 FXML 文档控制器有一个 buttonListerner,它调用:

longCalculationThread thread = new longCalculationThread(threadBundle);
thread.start();

longcalculationthread 创建一个包含大约一百万个素数的列表,并使用以下代码将它们导出到 ExcelWriter:

private void publishResults() throws IOException, InterruptedException{
    if (!longResults.isEmpty()){
        if (shouldExport) {
            progressText.setText("Exporting to Excel...");
            ExcelWriter.makeSpreadsheet(longResults, exportType, excelExportLimit, getTitle(), progressBar);

    }
   }

问题是,即使 XSSF 工作簿中保存工作簿的变量是使用它的方法的局部变量,它之后也不会被垃圾收集。

它占用了大约 1.5GB 的 RAM(我不知道为什么),并且只有在调用另一个大型导出时才会重新分配数据(不适用于小型导出)。 我的问题并不是这个东西需要大量的 RAM,而是即使方法完成,内存也不会被 GC。 以下是我的 NetBeans 配置文件的一些图片:

制作 1000000 个素数的数组时的正常内存使用情况:

制作工作簿时使用大量堆

当无法再访问工作簿时,不会重新分配内存

使用相同的静态方法制作新工作簿时出现波动

【问题讨论】:

    标签: java memory memory-leaks garbage-collection fileoutputstream


    【解决方案1】:

    我找到了答案!我不得不用 System.gc() 提示 GC。我记得之前尝试过这个,但是我必须把它放在一个工作簿仍然可以访问并且因此不能被 GCed 的地方。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-29
      • 2017-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多