【问题标题】:Memory not freed up, after closing java InputStream [duplicate]关闭java InputStream后内存未释放[重复]
【发布时间】:2019-02-01 19:29:30
【问题描述】:

在这段代码的 sn-p 中,我使用 Apache POI 库加载了一个大小为 10MB 的 excel 文件。这消耗了将近 2GB 的内存。 在遍历了所有行之后,我终于调用了 close 方法。但是,似乎 GC 并没有释放此流和对象消耗的空间。并且仍然使用 2GB + 400MB 内存。

有什么想法吗?

这是我的代码:

public List<Meter> loadFile(File excelFile) throws IOException, InvalidFormatException {
    List<Meter> allMeters = new ArrayList<>();
    InputStream inputStream = new FileInputStream(excelFile);
    XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
    Sheet sheet1 = workbook.getSheetAt(0);
    Iterator<Row> rows_sheet1 = sheet1.iterator();
    if (rows_sheet1.hasNext()) {
        rows_sheet1.next(); //skip header
    }

    while (rows_sheet1.hasNext()) {
        try {
            Row currentRow = rows_sheet1.next();
            Cell meterNoCell = currentRow.getCell(0);
            Cell startPeriodCell = currentRow.getCell(1);
            Cell endPeriodCell = currentRow.getCell(2);
            Cell previousConsumption = currentRow.getCell(3);
            Cell currentConsumption = currentRow.getCell(4);
            Cell periodConsumptionCell = currentRow.getCell(5);

            meterNoCell.setCellType(CellType.STRING);
            startPeriodCell.setCellType(CellType.STRING);
            endPeriodCell.setCellType(CellType.STRING);

            //Reading values from above_defined cells and filling allMeters list (defined at the begining of the function).
            //......
            //Done
        }
        catch (Exception ex) {
            Logger.getLogger(MetersList.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    workbook.close();
    inputStream.close();
    return allMeters;
}

【问题讨论】:

  • 你如何衡量内存使用情况?
  • 进行内存转储并分析它以了解您的内存中剩余的对象类型
  • 请注意,每次close()操作后GC不会自动调用,它是由JVM调用的,调用取决于它的GC设置。您可以使用jvisualvm 来监控您的Java 进程,在那里您可以找到“Perform GC”按钮。请尝试一下,您会发现 GC 后堆使用量减少了。
  • @Arman 任务管理器不会为您提供有关 GC 活动的信息 - JVM 将在操作系统内存中保留一些空间并根据需要进行增长。但是 java 进程使用的内存包括已使用和未使用的堆空间(即它不会在 GC 后减少)。您需要以不同的方式监控 GC,例如使用 jvisualvm 或飞行记录器。
  • 解引用inputStream和workbook可以更早触发GC,试一试。

标签: java memory garbage-collection out-of-memory inputstream


【解决方案1】:

首先,我注意到使用任务管理器 (Windows) 或活动监视器 (Mac) 进行监控是一项愚蠢的工作。这些工具显示的是保留的堆空间(而不是使用的堆空间)。因此,当我使用 NetBeans 分析监视我的应用程序的内存使用情况时,我注意到 GC 工作得非常好并且释放了堆内存。

此外,取消引用 WorkbookInputStream 对象(使用 =null;)加速了 GC 执行。

在那之后,我的问题发生了变化。

关闭这些流后,GC 运行良好,使用的堆空间减少。但是保留的堆空间会保持不变,不会减少,如下图所示:

我看了this article。综上,需要用到以下JVM参数:

-XX:MinHeapFreeRatio
-XX:MaxHeapFreeRatio

我设置了-XX:MaxHeapFreeRatio=40,过了一会儿,保留的堆空间被释放了。

【讨论】:

  • 这很正常:它依赖于JVM,并且一旦GCed它们不会立即向OS释放内存。即参见quora.com/… 另请注意,根据 jvm 类型,您还可以使用功能强大的分析器,称为任务控制飞行记录器 wiki.openjdk.java.net/display/jmc/Overview
  • 感谢@DavideCavestro
【解决方案2】:

尝试两个建议的解决方案,如果其中任何一个对您有帮助,请告诉我们。

  1. 对工作簿和 inputStream 使用 finalize() 而不是 close()。

  2. 关闭workbook和inputStream后都设置为null,然后调用System.gc();

【讨论】:

  • 不需要使用finalize(),而且应该避免使用它。请参阅 J. Bloch 的“Effective Java”一书。 close() 调用在这里就足够了。 System.gc() 不应在生产代码中调用,它可用于调试以确保 GC 消除死对象。它可以通过jvisualvm 等检测工具触发,因此无需污染您的代码。
  • finalize() 也受到保护,deprecated since Java 9。它不打算从用户代码中调用。
  • @DidierL 更好,XSSFWorkbook 不会覆盖 finalize(这是一件好事,否则它会被破坏),所以在它上面调用 finalize 不会关闭它全部。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-15
  • 2018-12-09
  • 2013-01-02
  • 2011-09-18
  • 2013-02-11
  • 2017-02-01
相关资源
最近更新 更多