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

标签 java memory-leaks apache-poi xssf

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

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

  • 它将加载 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");
            }
        }
    };


该类中读取/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();
    }
}}


我无法弄清楚问题出在哪里。 我正在关闭工作簿/输入流/输出流并清除 Arraylist。

最佳答案

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

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。通常,从操作系统分配该内存后,JVM 不会将其归还(经常)。但是,如果您查看堆使用情况(使用 JConsole 或 JVisual VM 等工具),您会发现堆在 GC 之后被回收。

Java如何消耗内存

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

JVisual VM Memory

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

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

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

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

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

限制 Java 可以使用的(操作系统)内存

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

关于使用 Apache.POI 读写 Excel 的 Java 内存问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34850130/

相关文章:

java - 正则表达式只接受字母和空格,不允许在字符串的开头和结尾有空格

java - cvHaarDetectObjects() 方法有什么作用?

java - 如果无法找到/放置运行给定的方面文件,如何强制 AspectJ 抛出运行时错误?

node.js - 部署到 Bluemix 的简单 node.js 应用程序似乎正在泄漏内存

c++ - 为什么这段代码不会导致内存泄漏?

android - 空 Activity 中的内存泄漏

java - java日历分钟设置意外值

java - 导出的 .jar 上的 Apache POI 未在其他计算机上运行

java - Apache POI 样式应用于所有单元格

java - 如何使用 Java Apache POI 将日期格式从 04-May-2016 转换为 04/05/2016