为了账号安全,请及时绑定邮箱和手机立即绑定

捕获java.lang.OutOfMemoryError?

捕获java.lang.OutOfMemoryError?

SMILET 2019-07-01 10:03:30
捕获java.lang.OutOfMemoryError?文献资料为java.lang.Error说:错误是Throwable的子类,它指示合理的应用程序不应该试图捕捉的严重问题。但是.java.lang.Error是java.lang.Throwable,我能抓住这种类型的“可扔”(Throwable)。我明白为什么抓住这种异常不是个好主意。据我所知,如果我们决定捕获它,CATCH处理程序不应该单独分配任何内存。不然的话OutOfMemoryError将再次被抛出。所以,我的问题是:当捕捉到java.lang.OutOfMemoryError也许是个好主意?如果我们决定抓到java.lang.OutOfMemoryError,我们如何确保CATCH处理程序本身不分配任何内存(任何工具或最佳实践)?
查看完整描述

3 回答

?
阿晨1998

TA贡献2037条经验 获得超6个赞

我同意和不同意这里的大多数答复。

在许多情况下,您可能希望捕获OutOfMemoryError根据我的经验(在Windows和Solaris JVM上),很少有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/247M99 : Memory Use :0M/247M100 : Memory Use :0M/247M

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

但要注意的是,只有在可能进行清理的地方才能捕获这些类型的错误。不要毛毯catch(Throwable t) {}到处乱七八糟的。


查看完整回答
反对 回复 2019-07-01
?
UYOU

TA贡献1878条经验 获得超4个赞

你,你们能,会,可以从它中恢复过来:

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足够的内存即可。必要时运行分析器。


查看完整回答
反对 回复 2019-07-01
?
LEATH

TA贡献1936条经验 获得超6个赞

一般来说,尝试从OOM中捕获和恢复是一个坏主意。

  1. OOME也可以抛到其他线程上,包括应用程序甚至不知道的线程。任何这样的线程现在都会死掉,任何等待通知的东西都可能永远被卡住。简而言之,你的应用程序可能会被彻底破坏。

  2. 即使您确实成功地恢复了,您的JVM可能仍然处于堆饥饿状态,因此您的应用程序将执行得很糟糕。

使用OOME最好的方法是让JVM死掉。

(这假设JVM是吗?去死吧。例如,Tomcatservlet线程上的Ooms不会杀死JVM,这会导致Tomcat进入紧张状态,在那里它不会响应任何请求.甚至不请求重新启动)。

编辑

我并不是说抓OOM是个坏主意。当您试图从OOME中恢复时,无论是故意的还是由于疏忽,都会出现问题。无论何时捕获OOM(直接,或作为错误或Throwable的子类型),您都应该重新抛出OOM,或者安排应用程序/JVM退出。

旁白:这意味着为了在面向对象的情况下获得最大的健壮性,应用程序应该使用Thread.setDefaultUncoghtExceptionHandler()设置将导致应用程序在发生OOME时退出的处理程序,无论OOME抛出在哪个线程上。我会对此有兴趣的.

唯一的另一种情况是当你知道当然了OOM没有造成任何附带损害;也就是说,你知道:

  • 是什么导致了OOME,
  • 应用程序当时正在做什么,简单地放弃计算是可以的,并且
  • 一个(大致)同时的OOME不可能发生在另一个线程上。

有些应用程序有可能知道这些事情,但对于大多数应用程序,您无法确定在OOME之后继续使用是安全的。即使当你尝试它时,它在经验上是“有效的”。

(问题是,需要有正式证据证明“预期”OOME的后果是安全的,而“意外”OOME不能在尝试/捕获OOME的控制范围内发生。


查看完整回答
反对 回复 2019-07-01
  • 3 回答
  • 0 关注
  • 919 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信