文档 for java.lang.Error说:
错误是Throwable的子类,它指示合理的应用程序不应该试图捕捉的严重问题。
但是由于java.lang.Error是java.lang.Throwable的子类,所以我可以捕获这种类型的Throwable。
我明白为什么抓到这种异常不是个好主意。据我所知,如果我们决定捕获它,catch处理程序不应该单独分配任何内存。否则,将再次引发OutOfMemoryError。
所以,我的问题是:
java.lang.OutOfMemoryError时是否有任何真实的场景可能是个好主意?java.lang.OutOfMemoryError,如何确保catch处理程序本身不分配任何内存(任何工具或最佳实践)?发布于 2010-04-21 06:33:10
在很多情况下,您可能希望捕获一个OutOfMemoryError,根据我的经验(在OutOfMemoryError和Solaris上),OutOfMemoryError是JVM的丧钟,这种情况很少发生。
捕获OutOfMemoryError只有一个很好的理由,那就是优雅地关闭,干净地释放资源,并尽可能地记录失败的原因(如果仍然有可能的话)。
通常,OutOfMemoryError是由于无法满足堆的剩余资源的块内存分配而发生的。
当抛出Error时,堆包含的分配对象数量与未成功分配之前相同,现在是删除对运行时对象的引用以释放清理所需的更多内存的时候了。在这些情况下,它甚至可能继续,但这肯定是一个坏主意,因为您永远无法100%肯定JVM处于可修复状态。
演示OutOfMemoryError并不意味着JVM在catch块中内存不足:
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如果运行一些关键的内容,我通常会捕获Error,将其记录到syserr,然后使用我选择的日志框架对其进行日志记录,然后继续释放资源并以干净的方式关闭。最坏的情况是什么?JVM无论如何都快死了(或者已经死了),通过捕获Error,至少有机会进行清理。
但要注意的是,只有在可能进行清理的地方才能捕获这些类型的错误。不要到处覆盖catch(Throwable t) {},也不要像那样胡说八道。
发布于 2010-04-21 00:36:15
你可以从它中恢复过来:
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,如何确保catch处理程序本身不分配任何内存(任何工具或最佳实践)?
这取决于是什么导致了OOME。如果它是在try块之外声明的,并且是一步一步地发生的,那么您的机会就很小了。您可能需要事先预留一些内存空间:
private static byte[] reserve = new byte[1024 * 1024]; // Reserves 1MB.然后在OOME期间将其设置为零:
} catch (OutOfMemoryException e) {
     reserve = new byte[0];
     // Ha! 1MB free!
}当然,这完全没有意义;)只要根据应用程序的需要给JVM足够的内存即可。必要时运行分析器。
发布于 2010-04-20 23:30:45
一般来说,尝试从OOM中捕获和恢复是一个坏主意。
使用OOME最好的方法是让JVM死掉。
(这假定JVM确实死掉了。例如,Tomcat线程上的OOMs不会杀死JVM,这会导致Tomcat进入紧张状态,在那里它不会响应任何请求.甚至不请求重新启动)。)
编辑
我并不是说抓OOM是个坏主意。当您试图从OOME中恢复时,无论是故意的还是由于疏忽,都会出现问题。无论何时捕获OOM (直接,或作为错误或Throwable的子类型),您都应该重新抛出OOM,或者安排应用程序/ JVM退出。
旁白:这意味着,为了在面向对象的情况下实现最大的健壮性,应用程序应该使用Thread.setDefaultUncaughtExceptionHandler()设置一个处理程序,在发生OOME的情况下导致应用程序退出,而不管OOME抛出在哪个线程上。我会对此有兴趣.
唯一的其他情况是,当您确信OOM没有造成任何附带损害时,即您知道:
有些应用程序有可能知道这些事情,但对于大多数应用程序,您无法确定在OOME之后继续使用是安全的。即使当你尝试它时,它在经验上是“有效的”。
(问题是,需要有正式证据证明“预期”OOME的后果是安全的,而“意外”OOME的后果不能在尝试/捕获OOME的控制范围内发生。
https://stackoverflow.com/questions/2679330
复制相似问题