Java 相当于 CLR 的 UnhandledException 事件

发布于 2024-09-06 14:56:23 字数 2521 浏览 7 评论 0原文

在 CLR(C#、VB.NET 等使用的运行时)中,有一种方法可以注册一个回调,以便在引发未处理的异常时调用该回调。

Java中有类似的东西吗?

我猜想它可能是一些 API,您可以将一个对象传递给该 API,该对象使用单个方法实现某个接口。当抛出异常并且堆栈上没有匹配的catch时,运行时将调用注册对象上的方法,并传递异常对象。

这将允许程序员保存堆栈跟踪。它还允许他们调用 System.exit 来停止仅针对未处理的异常执行的finally 块。

更新 1。

为了说明这一点,这里有一个 C# 示例:

// register custom handler for unhandled exceptions
AppDomain.CurrentDomain.UnhandledException += (sender, evt) =>
{
    Console.WriteLine("unhandled exception");
    Environment.FailFast(null);
};

try
{
    throw new NullReferenceException();
}
finally
{
    Console.WriteLine("finally is executing");
}

要点是,通过调用 Environment.FailFast(null) 我可以停止 finally 阻止执行。

果然,在 Windows 7 上运行的 NET 3.5 和 4.0 中,我在输出中没有看到“finally isexecuting”字符串。但是,如果我注释掉 FailFast 调用,那么我确实会在输出中看到该字符串。

更新 2.

根据迄今为止的答案,我尝试用 Java 重现此问题。

// register custom handler for unhandled exceptions
Thread.currentThread().setUncaughtExceptionHandler(

    new Thread.UncaughtExceptionHandler() {

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

            System.out.println("Uncaught exception");
            System.exit(0);
        }
    }
);

try
{
    throw new NullPointerException();
}
finally
{
    System.out.println("finally is executing");
}

当我在 Java 6 (1.6.0_18) 中运行它时,我看到:

  • finally 正在执行
  • 未捕获的异常

换句话说,JRE 在执行未捕获的异常处理程序之前执行 finally 块。

对于为什么这很重要的一些背景,这里有一个更复杂的例子:

try
{
    try
    {
        throw new NullPointerException();
    }
    finally
    {
        System.out.println("finally is executing");
        throw new java.io.IOException();
    }
}
catch (java.io.IOException x)
{
    System.out.println("caught IOException");
}

System.out.println("program keeps running as if nothing had happened...");

所以有一个严重的错误,我希望我的程序停止并记录堆栈跟踪。但在我执行此操作之前,堆栈上的某个位置有一个中间 finally 块(在实际程序中,它将位于单独的方法中),并且它会尝试访问文件系统。出了问题。然后再往上一点,假设我捕获了 IOException,因为它们对我来说没什么大不了的。

不用说,输出是:

  • finally正在执行
  • catched IOException
  • 程序继续运行,好像什么也没发生一样......

所以现在我不小心创造了一种情况,其中严重的错误对我来说是隐藏的。

有两种解决方案:

  • 以某种方式确保finally阻止永远抛出,因为它们无法在本地判断它是否安全。这是一种耻辱,因为它们在正常执行路径上抛出是完全可以的,即当它们没有运行以响应先前的异常时。
  • 告诉运行时我不希望它在出现未捕获的异常时运行 finally 块。

如果可以的话,后者当然是我的首选。

In the CLR (the runtime used by C#, VB.NET, etc.) there's a way of registering a callback to be called when an unhandled exception is thrown.

Is there anything similar in Java?

I'm guessing it would presumably be some API to which you'd pass an object that implements some interface with a single method. When an exception is thrown and there is no matching catch on the stack, the runtime would call the method on the registered object, and would be passed the exception object.

This would allow the programmer to save the stack trace. It would also allow them to call System.exit, to stop finally blocks executing only for unhandled exceptions.

Update 1.

To illustrate this, here is a sample in C#:

// register custom handler for unhandled exceptions
AppDomain.CurrentDomain.UnhandledException += (sender, evt) =>
{
    Console.WriteLine("unhandled exception");
    Environment.FailFast(null);
};

try
{
    throw new NullReferenceException();
}
finally
{
    Console.WriteLine("finally is executing");
}

The point is that by putting in the call to Environment.FailFast(null) I can stop the finally block from executing.

Sure enough, in NET 3.5 and 4.0 running on Windows 7, I don't see the "finally is executing" string in the output. But if I comment out the FailFast call, then I do see that string in the output.

Update 2.

Based on the answers so far, here's my attempt to reproduce this in Java.

// register custom handler for unhandled exceptions
Thread.currentThread().setUncaughtExceptionHandler(

    new Thread.UncaughtExceptionHandler() {

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

            System.out.println("Uncaught exception");
            System.exit(0);
        }
    }
);

try
{
    throw new NullPointerException();
}
finally
{
    System.out.println("finally is executing");
}

When I run that in Java 6 (1.6.0_18) I see:

  • finally is executing
  • uncaught exception

In other words, the JRE executes finally blocks before executing the uncaught-exception handler.

For some background on why this is important, here's a more complex example:

try
{
    try
    {
        throw new NullPointerException();
    }
    finally
    {
        System.out.println("finally is executing");
        throw new java.io.IOException();
    }
}
catch (java.io.IOException x)
{
    System.out.println("caught IOException");
}

System.out.println("program keeps running as if nothing had happened...");

So there's a serious bug and I want my program to halt and log the stack trace. But before I can do this, there's an intermediate finally block somewhere on the stack (in a real program it would be in a separate method) and it tries to access the file system. Something goes wrong. Then a little further up the stack suppose I have a catch for IOException because they're not a big deal to me.

Needless to say, the output is:

  • finally is executing
  • caught IOException
  • program keeps running as if nothing had happened...

So now I have by accident created a situation in which serious bugs are hidden from me.

There are two solutions:

  • somehow ensure that finally blocks never throw, because they can't locally tell if it is safe to. This is a shame because it is perfectly okay for them to throw on the normal execution path, i.e. when they are not running in response to a previous exception.
  • tell the runtime that I don't want it to run finally blocks when there's an uncaught exception.

The latter is certainly my preference if it's available.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

忘你却要生生世世 2024-09-13 14:56:23

请参阅 Thread.setUncaughtExceptionHandler()

更新:事实证明,“未处理/未捕获的异常”在 C# 和 Java 中的含义似乎略有不同。乍一看,您描述的行为在 Java 中似乎是正常的(对您来说不幸的是):

设置当该线程由于未捕获的异常而突然终止时调用的处理程序。

即,当异常已经从用户代码传播到线程中时,将调用未捕获的异常处理程序。这意味着它留下了包含finally 块的方法,该方法已被正确执行。

AFAIK finally总是在 Java 中运行,因此您的选项 2 不可行。

下面讨论的结论是

选项 1 - 不在 finally 块中抛出任何内容 - 尽管有限制,但似乎是唯一真正的长期解决方案。

对于简单的日志记录部分,有一个选项 3:

try
{
    try
    {
        throw new NullPointerException();
    }
    catch (Exception x)
    {
        System.out.println("caught Exception" + x.getMessage());
        x.printStackTrace();
        throw x; // keep original behaviour
    }
    finally
    {
        System.out.println("finally is executing");
        throw new java.io.IOException();
    }
}
catch (java.io.IOException x)
{
    System.out.println("caught IOException");
}

See Thread.setUncaughtExceptionHandler().

Update: as it turned out, the "unhandled / uncaught exception" seems to mean slightly different things in C# and Java. At first sight, the behaviour you describe seems to be (unfortunately for you) normal in Java:

Set the handler invoked when this thread abruptly terminates due to an uncaught exception.

I.e. the uncaught exception handler is called when the exception has already been propagated out of user code, up into the Thread. This implies that it left the method containing the finally block, which has been duly executed.

AFAIK finally blocks are always run in Java, so your Option 2 is not viable.

Conclusion from the discussion below

Option 1 - not throwing anything in finally blocks -, though limiting, seems to be the only real long term solution.

For plainly the logging part, there is an Option 3 though:

try
{
    try
    {
        throw new NullPointerException();
    }
    catch (Exception x)
    {
        System.out.println("caught Exception" + x.getMessage());
        x.printStackTrace();
        throw x; // keep original behaviour
    }
    finally
    {
        System.out.println("finally is executing");
        throw new java.io.IOException();
    }
}
catch (java.io.IOException x)
{
    System.out.println("caught IOException");
}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文