Java 使用 Apache.POI 读写 Excel 的内存问题

Java Memory issue using Apache.POI to read write Excel

我正在尝试读取 excel 文件...进行一些更改...保存到新文件。

我创建了带有按钮的小表单...按下按钮时..

现在的问题是内存问题。
加载表单时,我可以在 windows 任务管理器中看到...javaw 使用了大约 23MB。
在读写过程中excel...内存飙升至 170MB。
清空数组列表后....内存未清空并保持在 150MB 左右。

以下代码附加到按钮单击事件。

MouseListener mouseListener = new MouseAdapter() {
        public void mouseReleased(MouseEvent mouseEvent) {
            if (SwingUtilities.isLeftMouseButton(mouseEvent)) {
                ArrayList<Address> addresses = ExcelFunctions.getExcelData(fn);
                for (Address address : addresses){
                    address.setZestimate(Integer.toString(rnd.nextInt(45000)));
                    address.setRedfinestimate(Integer.toString(rnd.nextInt(45000)));
                }
                ExcelFunctions.saveToExcel(ofn,addresses);
                addresses.clear();
                JOptionPane.showMessageDialog(null, "Done");
            }
        }
    };


此 Class 中 Reading/Excel 文件的代码。

public class ExcelFunctions {
public static ArrayList<Address> getExcelData(String fn)
{
    ArrayList<Address> output = new ArrayList<Address>();
    try
    {
        FileInputStream file = new FileInputStream(new File(fn));

        //Create Workbook instance holding reference to .xlsx file
        XSSFWorkbook workbook = new XSSFWorkbook(file);

        //Get first/desired sheet from the workbook
        XSSFSheet sheet = workbook.getSheetAt(0);
        System.out.println(sheet.getSheetName());
        //Iterate through each rows one by one
        Iterator<Row> rowIterator = sheet.iterator();
        while (rowIterator.hasNext())
        {
            Row row = rowIterator.next();
            int r = row.getRowNum();
            int fc= row.getFirstCellNum();
            int lc = row.getLastCellNum();
            String msg = "Row:"+ r +"FColumn:"+ fc + "LColumn"+lc;
            System.out.println(msg);
            if (row.getRowNum() > 0) {
                Address add = new Address();
                Cell c0 = row.getCell(0);
                Cell c1 = row.getCell(1);
                Cell c2 = row.getCell(2);
                Cell c3 = row.getCell(3);
                Cell c4 = row.getCell(4);
                Cell c5 = row.getCell(5);
                if (c0 != null){c0.setCellType(Cell.CELL_TYPE_STRING);add.setState(c0.toString());}
                if (c1 != null){c1.setCellType(Cell.CELL_TYPE_STRING);add.setCity(c1.toString());}
                if (c2 != null){c2.setCellType(Cell.CELL_TYPE_STRING);add.setZipcode(c2.toString());}
                if (c3 != null){c3.setCellType(Cell.CELL_TYPE_STRING);add.setAddress(c3.getStringCellValue());}
                if (c4 != null){c4.setCellType(Cell.CELL_TYPE_STRING);add.setZestimate(c4.getStringCellValue());}
                if (c5 != null){c5.setCellType(Cell.CELL_TYPE_STRING);add.setRedfinestimate(c5.getStringCellValue());}
                output.add(add);
                c0=null;c1=null;c2=null;c3=null;c4=null;c5=null;
            }
        }
        workbook.close();
        file.close();
    }
    catch (Exception e)
    {
        System.out.println(e.getMessage());
    }
    return output;
}

public static void saveToExcel(String ofn, ArrayList<Address> addresses) {
    XSSFWorkbook workbook = new XSSFWorkbook();
    XSSFSheet sheet = workbook.createSheet("Addresses");

    Row header = sheet.createRow(0);
    header.createCell(0).setCellValue("State");
    header.createCell(1).setCellValue("City");
    header.createCell(2).setCellValue("Zip");
    header.createCell(3).setCellValue("Address");
    header.createCell(4).setCellValue("Zestimates");
    header.createCell(5).setCellValue("Redfin Estimate");

    int row = 1;

    for (Address address : addresses){
        Row dataRow = sheet.createRow(row);
        dataRow.createCell(0).setCellValue(address.getState());
        dataRow.createCell(1).setCellValue(address.getCity());
        dataRow.createCell(2).setCellValue(address.getZipcode());
        dataRow.createCell(3).setCellValue(address.getAddress());
        dataRow.createCell(4).setCellValue(address.getZestimate());
        dataRow.createCell(5).setCellValue(address.getRedfinestimate());
        row++;
    }


    try {
        FileOutputStream out =  new FileOutputStream(new File(ofn));
        workbook.write(out);
        out.close();
        workbook.close();
        System.out.println("Excel with foumula cells written successfully");

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}}


我无法弄清楚问题出在哪里。 我正在关闭 workbook/inputstream/outputstream 并清除 Arraylist。

在新版本的 poi 中,他们使用 Java 流来解决内存问题。Have a look

你可能没有内存泄漏...

When form is loaded, I can see in windows task manager...javaw is using around 23MB. During read and write excel...memory shoots upto 170MB. After array list is cleared....Memory is not clearing up and stays around 150MB.

这不是内存泄漏 - 任务管理器显示的是进程保留的内存 - 而不是应用程序 heap space

您的 JVM 将分配堆至其配置的最大值,比如 200 MiB。通常,在从 OS 分配此内存后,JVM 不会(经常)归还它。但是,如果您查看堆使用情况(使用 JConsole 或 JVisual VM 之类的工具),您会发现堆在 GC 后被回收。

Java如何消耗内存

作为一个非常基本的例子:

图片来源:https://stopcoding.files.wordpress.com/2010/04/visualvm_hfcd4.png

在此示例中,JVM 有一个 1 GiB 的最大堆,并且由于应用程序需要更多内存,因此从 OS(橙色区域)保留了 400 MiB。

蓝色区域是应用程序实际使用的堆内存。 saw-tooth 效果是垃圾收集过程回收未使用内存的结果。请注意,橙色区域保持相当稳定 - 它通常不会随着每次 GC 事件而调整大小...

within few seconds...it shoot upto 800MB and stays there till end....I have not got any memory error

如果发生内存泄漏,最终会出现内存不足错误。 "leak"(至少在 Java 中)是应用程序占用堆中的内存,但不释放它以供应用程序重用。如果您观察到的内存增长如此之快,但应用程序没有崩溃,您可能会看到内部(在 JVM 中)内存实际上正在被释放和重用。

限制内存Java可以使用多少(OS)

如果你想限制你的应用程序可以从 OS 保留的内存,你需要配置你的最大堆大小(通过 -Xmx 选项)以及你的永久代大小(如果您仍在使用 Java 7 或更早版本)。请注意,JVM 本身会使用一些内存,因此 OS 级别(使用任务管理器等工具)显示的值可能高于您指定的应用程序内存总和。