Apache POI XSSFWorkbook内存泄漏 [英] Apache POI XSSFWorkbook memory leak

查看:295
本文介绍了Apache POI XSSFWorkbook内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



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



此导出逻辑的静态方法保存在名为ExcelWriter的类中。基本上,它通过Arraylist参数进行迭代,并用其内容填充XSSFWorkbook。后缀,FileOutputStream被用来实际上使它成为一个excel文件。下面是它的相关部分:

  public class ExcelWriter {

//配置JFileChooser使在覆盖旧文件之前提醒
private static JFileChooser fileManager = new JFileChooser(){
@Override
public void approveSelection(){
...
}
};


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



//只有可以从外部调用才能访问这个类的功能的方法
public static< T extends Object>无效makeSpreadsheet
(ArrayList< T>列表,spreadsheetTypes类型,int max,String标题,JFXProgressBar progressBar)
抛出IOException,InterruptedException {
progressBar.progressProperty()。setValue(0);
switch(type){
case rightToLeftColumnLimit:
makeSpreadsheetRightToLeft(list,false,max,title,progressBar);
休息;
...
}
}


static private< T extends Object>无效makeSpreadsheetRightToLeft
(ArrayList< T> list,boolean maxRows,int max,String title,JFXProgressBar progressBar)
抛出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();

//给电子表格一个内部标题也是
Row row = sheet.createRow(0);
row.createCell(0).setCellValue(title);

row = sheet.createRow(++ rowPointer);

//如果(!数字列表){
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 {
//使工作表具有最大行限制
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);




$ b static private void writeToExcel(XSSFWorkbook book,JFXProgressBar progressBar)throws IOException,InterruptedException {
//导出到Excel
int returnValue = fileManager.showSaveDialog(null);

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

//验证逻辑在这里


尝试{

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

}

}
}
}

$ b

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

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

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

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





$ p
$ b

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



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

创建1000000个素数数组时的正常内存使用情况: < a href =https://i.stack.imgur.com/9wKTF.png =nofollow noreferrer>



制作工作簿时使用大量的堆



当工作簿不可再使用时,内存不会被重新分配



使用相同静态方法制作新工作簿时出现波动
<啊ref =https://i.stack.imgur.com/pXBI2.png =nofollow noreferrer>

解决方案

我找到了答案!我不得不用System.gc()提示GC。我记得早些时候尝试过,但是我必须把它放在一个工作簿仍然可以访问的速度,因此不能被GCed。


So I'm making a large-scale prime number generator in Java (with the help of JavaFX).

It uses the Apache POI library (I believe I'm using v3.17) to output the results to Excel spreadsheets.

The static methods for this exporting logic are held in a class called ExcelWriter. Basically, it iterates through an Arraylist arguments and populates a XSSFWorkbook with it's contents. Afterwords, a FileOutputStream is used to actually make it an excel file. Here are the relevant parts of it:

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){

        } 

    }
}
}

Afterwards, my FXML document controller has a buttonListerner which calls:

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

The longcalculationthread creates a list of about a million prime numbers and Exports them to the ExcelWriter using this code:

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

    }
   }

The problem is, even though the variable holding the workbook in the XSSF workbook is a local variable to the methods it is used in, it doesn't get garbage collected afterwards.

It takes up like 1.5GB of RAM (I don't know why), and that data is only reallocated when another huge export is called (not for small exports). My problem isn't really that the thing takes a lot of RAM, it's that even when the methods are completed the memory isn't GCed. Here are some pictures of my NetBeans profiles:

Normal memory usage when making array of 1000000 primes:

Huge heap usage when making workbook

Memory Isn't reallocated when workbook ins't accessible anymore

Fluctuation seen when making a new workbook using the same static methods

解决方案

I found the answer! I had to prompt the GC with System.gc(). I remember trying this out earlier, however I must have put it in a pace where the workbook was still accessible and hence couldn't be GCed.

这篇关于Apache POI XSSFWorkbook内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆