【问题标题】:Runtime.freeMemory() and company or early allocation to prevent late out of memory errorsRuntime.freeMemory() 和公司或提前分配,以防止后期内存不足错误
【发布时间】:2020-06-02 23:39:03
【问题描述】:

我正在编写一个程序,根据 JVM、堆大小和用户设置,它可能会经常抛出一些 OOM 错误。

我不想给它一些任意的限制,但是因为当这个错误发生时,程序可能需要很长时间才能产生一些有意义的输出,我希望它至少退出如果OOM 即将发生,请尽快。我的第一种方法是尽早初始化最消耗内存的对象,这可能不是一个好习惯。

由于以后可能需要更多内存,具体取决于用户输入,我还想确保一些合理安全的可用内存余量,经过一些糟糕的想法后,我发现使用 Runtime 中的方法并给用户一些关于剩余内存的数据,关于可以从该数据中得到什么的数据,并在需要时发出警告将是一个更优雅的解决方案。

早期初始化的对象之一是用于写入音频文件的数组,直到程序结束才使用它,我考虑将其大小添加到空闲内存计算中,并仅在需要时对其进行初始化。

据我所知,之前计算空闲内存的主要缺陷如下:

  1. 并不完全准确
  2. 对于某些对象,堆内存中的实际大小无法预测,具体取决于实现
  3. 在极端情况下,程序仍将在计算结束时终止,当对象初始化时,不会在没有足够的内存完成程序时立即终止。重复检查可能是一种解决方案,但这会影响性能。

早期初始化的主要问题如下:

  1. 它会冻结一些未使用的资源,可能会影响性能。

  2. 可能会影响代码的可读性和可维护性,尤其是在误用和/或在大型程序中时。

  3. 导致程序总是以错误退出而不是阻止它并以优雅的方式退出。

在大多数情况下,解决此类问题的最佳方法是什么,是否有更好的解决方案或其他应考虑的因素,或者我提到的某些因素比我认为的重要或重要?

PS 我可能可以并且应该改进内存管理,这将在大多数情况下缓解或几乎解决问题,关心内存管理应该是优先事项,但我认为这仍然是一个有效的问题。我知道 Runtime.freeMemory() 不能像乍一看那样使用。

编辑

我将尝试澄清早期 OOM 的含义。

内存块 A(从一开始就使用,可以通过选项合理预测)

内存块 B(可变,取决于用户输入)

内存块 C(最后使用,可通过选项合理预测)

正常方式:A ,B ,长时间计算,C (这里很可能会发生OOM)

早期初始化:A ,C ,B (这里很可能会发生OOM),长时间计算

我错过了一个重要的点(并且在大多数现实世界的程序中可能超过其他点)是 A 和 B 的一些内存可能会在使用 C 之前被释放,早期初始化会导致程序失败,即使没有需要它。

【问题讨论】:

  • 我不确定我是否遵循。 没有某种所需的内存预计算,应用程序应该如何知道 OOM 会发生?
  • 在另一种情况下,应用程序不知道 OOM 会发生,OOM 只是发生得更早,因为对象,至少是可预测的对象,是在使用之前很久就创建的。也欢迎对其他类型的内存预计算提出建议。
  • “我不想给它一些任意限制”:你为什么要给任意限制?您应该知道您的程序需要多少内存 + 保留一些回旋余地以解决不同 JVM 之间的差异。尝试预测 OOME 何时发生是危险的,应该避免。
  • 如果尝试预测 OOME 何时发生是危险的并且没有理由给它一些任意限制,我应该如何知道我的程序需要多少内存?

标签: java out-of-memory


【解决方案1】:

如果您希望程序在抛出OutOfMemoryError 时快速退出,请使用-XX:+ExitOnOutOfMemoryError-XX:+CrashOnOutOfMemoryError 选项。这将确保当您的程序中抛出 OOME 时,JVM 将退出。请参阅this article 了解这些选项之间的区别。

请注意,OutOfMemoryError 在少数情况下会抛出:没有足够的内存来分配对象、达到 GC 开销限制、达到文件描述符限制等。

【讨论】:

  • 我希望提前抛出错误,或者如果将来要抛出错误,程序以另一种方式退出,这样用户就不必等待长时间的计算而没有得到程序应该做什么。我看看能不能把问题说的更清楚。
  • @Harpistry 有一些方法可以“监听”java 堆,所以当老一代(比如说G1)达到一定的百分比时,你可以做任何你想做的事情。但无论哪种方式,我已经阅读了你的问题 3 次,但仍然不明白你想要实现什么。可惜,因为它看起来可能很有趣。
【解决方案2】:

如果您想根据应用程序内存不足的时间做不同的事情,您可以使用Thread.setDefaultUncaughtExceptionHandler(...)。 预关闭逻辑可能需要一些内存,您需要预先分配并在需要时释放。

byte[] memory = new byte[4096];
final AtomicBoolean shuttingDown = new AtomicBoolean();

Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
    if (throwable instanceof OutOfMemoryError) {
        if (shuttingDown.getAndSet(true)) return; // Execute the following logic only once
        memory = null; // Release pre-allocated memory
        // Do pre-shutdown logic
        System.exit(0);
    }
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-13
    • 1970-01-01
    • 2014-03-21
    • 1970-01-01
    相关资源
    最近更新 更多