Catching java.lang.OutOfMemoryError?

2022-08-31 09:06:32

文档 说:java.lang.Error

Error 是 Throwable 的一个子类,它指示合理的应用程序不应尝试捕获的严重问题

但是作为 的子类,我可以捕获这种类型的 Throwable。java.lang.Errorjava.lang.Throwable

我理解为什么捕捉这种异常不是一个好主意。据我所知,如果我们决定捕获它,则捕获处理程序不应自行分配任何内存。否则将再次抛出。OutOfMemoryError

所以,我的问题是:

  1. 是否有任何现实世界的场景,当捕捉可能是一个好主意?java.lang.OutOfMemoryError
  2. 如果我们决定 catch ,我们如何确保 catch 处理程序本身不会分配任何内存(任何工具或最佳实践)?java.lang.OutOfMemoryError

答案 1

在很多情况下,您可能希望抓住一个,根据我的经验(在Windows和Solaris JVM上),很少是JVM的丧钟。OutOfMemoryErrorOutOfMemoryError

只有一个很好的理由来捕获,那就是优雅地关闭,干净利落地释放资源并尽可能地记录失败的原因(如果仍然可以这样做)。OutOfMemoryError

通常,发生这种情况是由于块内存分配无法满足堆的剩余资源。OutOfMemoryError

抛出 时,堆包含与分配不成功之前相同数量的已分配对象,现在是时候删除对运行时对象的引用,以释放清理可能需要的更多内存。在这些情况下,甚至可以继续,但这肯定是一个坏主意,因为您永远无法100%确定JVM处于可修复状态。Error

演示这并不意味着 JVM 在 catch 块中内存不足:OutOfMemoryError

private static final int MEGABYTE = (1024*1024);
public static void runOutOfMemory() {
    MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    for (int i=1; i <= 100; i++) {
        try {
            byte[] bytes = new byte[MEGABYTE*500];
        } catch (Exception e) {
            e.printStackTrace();
        } catch (OutOfMemoryError e) {
            MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
            long maxMemory = heapUsage.getMax() / MEGABYTE;
            long usedMemory = heapUsage.getUsed() / MEGABYTE;
            System.out.println(i+ " : Memory Use :" + usedMemory + "M/" +maxMemory+"M");
        }
    }
}

此代码的输出:

1 : Memory Use :0M/247M
..
..
..
98 : Memory Use :0M/247M
99 : Memory Use :0M/247M
100 : Memory Use :0M/247M

如果运行一些关键的东西,我通常会抓住,将其记录到syserr,然后使用我选择的日志记录框架进行记录,然后继续释放资源并以干净的方式关闭。可能发生的最坏情况是什么?无论如何,JVM正在死亡(或已经死亡),并且通过捕获,至少有机会进行清理。ErrorError

需要注意的是,您必须仅在可以清理的地方捕获这些类型的错误。不要到处乱扔毯子,也不要胡说八道。catch(Throwable t) {}


答案 2

您可以从中恢复:

package com.stackoverflow.q2679330;

public class Test {

    public static void main(String... args) {
        int size = Integer.MAX_VALUE;
        int factor = 10;

        while (true) {
            try {
                System.out.println("Trying to allocate " + size + " bytes");
                byte[] bytes = new byte[size];
                System.out.println("Succeed!");
                break;
            } catch (OutOfMemoryError e) {
                System.out.println("OOME .. Trying again with 10x less");
                size /= factor;
            }
        }
    }

}

但这有意义吗?您还想做什么?为什么最初会分配那么多内存?更少的内存也可以吗?无论如何,你为什么不使用它呢?或者,如果这是不可能的,为什么不从一开始就给JVM更多的内存呢?

回到你的问题:

1:在捕获java.lang.OutOfMemoryError时,是否有任何真实的单词场景可能是一个好主意?

没有想到。

2:如果我们捕获java.lang.OutOfMemoryError,我们怎么能确定捕获处理程序本身不会分配任何内存(任何工具或最佳实践)?

这取决于是什么导致了OOME。如果它是在区块之外宣布的,并且它一步一步地发生,那么你的机会就很小了。您可能希望事先保留一些内存空间:try

private static byte[] reserve = new byte[1024 * 1024]; // Reserves 1MB.

然后在 OOME 期间将其设置为零:

} catch (OutOfMemoryException e) {
     reserve = new byte[0];
     // Ha! 1MB free!
}

当然,这一切都毫无意义;)只需根据您的应用需要为 JVM 提供足够的内存即可。如有必要,请运行探查器。


推荐