【问题标题】:Do errors thrown within UncaughtExceptionHandler get swallowed?UncaughtExceptionHandler 中抛出的错误会被吞没吗?
【发布时间】:2014-07-18 22:27:39
【问题描述】:

Thread.UncaughtExceptionHandler 声明当处理未捕获异常的方法本身抛出异常时,该异常将被忽略:

void uncaughtException(Thread t, Throwable e):

当给定线程由于给定而终止时调用的方法 未捕获的异常。

此方法抛出的任何异常都将被 Java 忽略 虚拟机。

但是当我测试它时,JVM并没有忽略未捕获的异常处理程序处理的异常:

public static void main(final String args[]) {
    Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread arg0, Throwable arg1) {
            throw new java.lang.RuntimeException("e2");
        }
    });
    throw new RuntimeException("e1");
}

Eclipse 控制台输出 (JRE 1.7):

异常:java.lang.RuntimeException 从 线程“main”中的 UncaughtExceptionHandler

我发现的另一个奇怪之处是我得到的输出不是来自System.err。它似乎完全来自另一个流。我通过将System.err 重定向到System.out 验证了这一点,但我仍然得到“红色”输出:

public static void main(final String[] args) {
    System.setErr(System.out);
    System.out.println(System.err == System.out);
    System.err.println("this is black color");
    try {
        throw new Error("test stacktrace color");
    } catch (Throwable e) {
        e.printStackTrace();
    }
    try {
        Thread.sleep(2500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            throw new RuntimeException("from handler");
        }
    });
    throw new RuntimeException("from main");
}

输出(粗体表示红色):

是的

这是黑色

java.lang.Error:在 asf.df.main(df.java:13) 处测试堆栈跟踪颜色

异常:从线程“main”中的 UncaughtExceptionHandler 抛出 java.lang.RuntimeException

这些现象的解释是什么?

UncaughtExceptionHandler 中抛出的错误会发生什么?预期(记录或保证)的行为是什么?

【问题讨论】:

  • 可能特定于版本?
  • 即使在以前的 Java 版本中,文档也声明它应该被 JVM 忽略。
  • 在 Oracle "1.6.0_39" / Ubuntu 12.04 中,未捕获的异常处理程序中抛出的异常被 JVM 吞噬。就像@Andrew_CS 所说,它可能是特定于版本/供应商的。
  • 定义“没有忽略”。它可能会打印堆栈跟踪,但不会退出调用这些方法的线程,就像 Swing 事件线程捕获、打印和忽略 RTE 而不是退出一样。
  • @EJP, "ignore" 是文档使用的关键字。他们没有定义它,但从我得到的输出来看,它可能“没有被忽略”。

标签: java exception-handling jvm uncaught-exception uncaughtexceptionhandler


【解决方案1】:

HotSpot JVM 打印从 UncaughtExceptionHandler 抛出的异常。 见JavaThread::exit

    if (HAS_PENDING_EXCEPTION) {
      ResourceMark rm(this);
      jio_fprintf(defaultStream::error_stream(),
            "\nException: %s thrown from the UncaughtExceptionHandler"
            " in thread \"%s\"\n",
            pending_exception()->klass()->external_name(),
            get_thread_name());
      CLEAR_PENDING_EXCEPTION;
    }

JVM 直接在 stderr 上打印这些异常,无​​论 System.err 状态如何——无论它是否被重定向。

好吧,这种警告不会影响应用程序——从这个意义上说,异常被“忽略”了。但你是对的,这种行为并不明显。 Javadoc 具有误导性,最好修复。

【讨论】:

  • +1,但不是反过来,HotSpot 应该被修复以遵守 JLS 吗?
  • 我认为默默地忽略异常并不是一个好主意。这种情况很难调试。顺便说一句,这只是一个 API 文档,它不是 JLS 或 JVMS 的一部分。随着实现的变化,Javadoc 往往会变得过时 - 由于性能优化、新特性等原因,这种情况经常发生。例如,据说默认的 hashCode 通常是通过转换内部对象地址来实现的,但对于热点 JVM。
【解决方案2】:

当从非主线程抛出时,异常被忽略并继续处理。

如果在 main 中抛出,则返回的错误代码非零。

未处理的异常通过 syserr 记录。

public static void main(final String[] args) {

    final Thread myThread = new Thread(new Runnable() {

        @Override
        public void run() {
            Thread.currentThread()
                .setUncaughtExceptionHandler(new UncaughtExceptionHandler() {

                    @Override
                    public void uncaughtException(final Thread t, final Throwable e) {

                        System.out.println("In child UncaughtExceptionHandler at " + java.time.Instant.now());

                        throw new RuntimeException("From child thread UncaughtExceptionHandler"
                                + java.time.Instant.now());

                    }
                });
            throw new RuntimeException("from runnable");
        }
    });

    Thread.currentThread()
    .setUncaughtExceptionHandler(new UncaughtExceptionHandler() {

        @Override
        public void uncaughtException(final Thread t, final Throwable e) {

                System.out.println("In main UncaughtExceptionHandler " + java.time.Instant.now());

                throw new RuntimeException("From main thread UncaughtExceptionHandler" + java.time.Instant.now());

        }
    });

    myThread.start();

    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));

    System.out.println("After child thread: " + java.time.Instant.now());

    //Will result in a non-zero return code
    throw new RuntimeException("from main");
}

输出:

在 2014-07-19T04:10:46.184Z 的子 UncaughtExceptionHandler 中

异常:从线程“Thread-0”中的 UncaughtExceptionHandler 抛出 java.lang.RuntimeException 子线程之后:2014-07-19T04:10:48.197Z 在主要 UncaughtExceptionHandler 2014-07-19T04:10:48.197Z

异常:从线程“main”中的 UncaughtExceptionHandler 抛出 java.lang.RuntimeException

【讨论】:

  • 从您的输出中,我们看到“异常:从线程 Thread-0 中的 UncaughtExceptionHandler 抛出的 java.lang.RuntimeException”。如果是这样,为什么说从非主线程抛出异常会被忽略?
  • 它写入 syserr 但在非主线程处理程序中抛出的异常不会传播,也不会在退出时修改返回码。从执行的角度来看,它们被忽略了。
  • 1) 它不会写入 system.error。我通过将 System.err 重定向到 System.out 来尝试此操作(请参阅问题中的第二个代码 sn-p),但输出仍然是红色。 2) 而且,显然在不同线程中抛出的异常不会相互传播,无论我们是否使用 UncaughtExceptionHandler ......
  • 如果在 Eclipse 的控制台中是红色的,除非默认值已经改变,这是标准错误。还验证了在 Eclipse 外部运行和管道到标准错误。我认为一旦从主系统流重定向(out,err)抛出异常就会丢失。
  • 不,(查看问题中的代码)我已经将 System.err 重定向到 System.out,因此 System.err.println 将打印黑色而不是红色。如果它是红色的,则表示它是另一个流,不再是 System.err。
猜你喜欢
  • 1970-01-01
  • 2011-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-30
  • 1970-01-01
相关资源
最近更新 更多