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

Muh*_*diq 2 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");
            }
        }
    };
Run Code Online (Sandbox Code Playgroud)


此类中读取/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();
    }
}}
Run Code Online (Sandbox Code Playgroud)


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

Mic*_*ael 5

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

加载表单后,我可以在 Windows 任务管理器中看到...javaw 使用了大约 23MB。在读写 excel 期间……内存高达 170MB。阵列列表被清除后....内存没有清除并保持在 150MB 左右。

这并不描述内存泄漏 - 任务管理器向您显示进程保留的内存 - 而不是应用程序堆空间

您的 JVM 会将堆分配到其配置的最大值,例如 200 MiB。通常,在从操作系统分配此内存后,JVM 不会将其返还(非常频繁)。但是,如果您查看堆使用情况(使用 JConsole 或JVisual VM 之类的工具),您将看到堆在 GC 后被回收。

Java如何消耗内存

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

JVisual VM 内存

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

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

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

在几秒钟内...它最多可以达到 800MB 并一直保持到最后....我没有遇到任何内存错误

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

限制 Java 可以使用的 (OS) 内存量

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