Java:当InterruptedException被扔进捕获块时,最后似乎两次被执行

发布于 2025-01-27 13:23:24 字数 399 浏览 2 评论 0原文

在以下代码中:

void foo() throws InterruptedException {
    try {
        f1();
    }
    catch (InterruptedException e) {
        f2();
        throw e;
    }
    finally {
        f3(); //breakpoint hit twice
    }
}

InterruptedExceptionf1()抛出,> 最后中的断点 block被击中两次,但是f3 ()在调试时仅输入一次。我想知道这是否正常。

In the following code:

void foo() throws InterruptedException {
    try {
        f1();
    }
    catch (InterruptedException e) {
        f2();
        throw e;
    }
    finally {
        f3(); //breakpoint hit twice
    }
}

When InterruptedException is thrown by f1(), the breakpoint in the finally block is hit twice, but f3() is entered only once while debugging. I'm wondering if this is normal.

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

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

发布评论

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

评论(2

魂归处 2025-02-03 13:23:24

这可能与尝试/最终的方式有关,当时将字节变成字节码(如.. jdk ... 6?它已经很长时间了,但是在遥远的过去,Opcodes JMP和RET用于此;这些opcodes不再由javac发射,并且很长时间以来一直没有出现)。

最后块只是重复。如果需要的话,很多时候。 Java基本上将其翻译成“抓住任何东西,运行最后的块,然后再重新捕捉到', +'...,并在Try Block的主体末尾复制此代码, +'...和在每个捕获块的末尾复制此代码'。

因此,在您的示例代码中,您的最后块实际上存在于您的编译字节 3次中中。您的代码是语法首先从:

   try {
        f1();
    }
    catch (InterruptedException e) {
        f2();
        throw e;
    }
    finally {
        f3(); //breakpoint hit twice
    }

to:

   try {
        f1();
        f3(); // CAVEAT 1
    }
    catch (InterruptedException e) {
        try {
          f2();
          f3(); // CAVEAT 2
        } catch (Throwable t) {
          f3();
          throw t;
        }
    }
    catch (Throwable t) {
        f3();
        throw t;
    }

caveat1:尽管有其他簿记,f3()并未重新启动以响应抛出异常本身。在Java代码中很难轻松编写,在字节码中,它的琐碎 - 通过声明“ opcodes范围”来尝试/捕获块?如果发生异常,请跳到此代码。 f3()在尝试块中的重复根本不是范围的一部分。

caveat2:生成的字节码足够有效,只能使每一个捕获块最终具有一次。

因此,您的类文件中有许多不同的字节码,它们都具有相同的“行号”。调试从根本上发生在字节码中(例如,当您设置断点时,选择一条行。但是,某些系统需要将该行转换为实际字节码,并分解字节码。在这种情况下,这很棘手:您实际上是一行,您实际上是断点点有3个完全不同的字节码

。显然,如果您想弄清楚为什么会发生,甚至可以撰写PR来解决该问题,那么这是我可以开始

的通过javap -c blocked block

/

class Test { void foo() {
    try {
        System.out.println("A");
    } catch (NullPointerException e) {
        System.out.println("B");
    } finally {
        System.out.println("C");
    }
}}

javac test.java < 代码>显示:

void foo();
    Code:
       0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #13                 // String A
       5: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      11: ldc           #21                 // String C
      13: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      16: goto          50
      19: astore_1
      20: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      23: ldc           #25                 // String B
      25: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      28: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      31: ldc           #21                 // String C
      33: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      36: goto          50
      39: astore_2
      40: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      43: ldc           #21                 // String C
      45: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      48: aload_2
      49: athrow
      50: return
    Exception table:
       from    to  target type
           0     8    19   Class java/lang/NullPointerException
           0     8    39   any
          19    28    39   any

分解:您可以轻松地查看c的加载方式三次,但是ab仅一次,这很奇怪鉴于源代码中仅提到C一次 - 直到您意识到最后块在整个地方都被复制。

This is possibly related to the way try/finally is contemporarily turned into bytecode (as in, since.. JDK... 6? It's been a long long time, but in the distant past, opcodes JMP and RET were used for this; those opcodes are no longer emitted by javac and haven't been for a very very long time).

The finally block is just repeated. Loads of times, if needed. java translates it to, basically, 'catch anything, run the finally block, then rethrow whatever you caught', + '... and duplicate this code at the end of the body of the try block', + '... and duplicate this code at the end of each and every catch block'.

Thus, in your example code, your finally block is actually present in your compiled bytecode 3 times. Your code is syntax desugared first from:

   try {
        f1();
    }
    catch (InterruptedException e) {
        f2();
        throw e;
    }
    finally {
        f3(); //breakpoint hit twice
    }

to:

   try {
        f1();
        f3(); // CAVEAT 1
    }
    catch (InterruptedException e) {
        try {
          f2();
          f3(); // CAVEAT 2
        } catch (Throwable t) {
          f3();
          throw t;
        }
    }
    catch (Throwable t) {
        f3();
        throw t;
    }

CAVEAT1: Though, with additional bookkeeping that f3() isn't re-invoked in response to throwing an exception itself. In java code that's hard to easily write, in bytecode its trivial - try/catch blocks work by declaring 'this range of opcodes? Jump to this code if exceptions occur'. The f3() duplication in the try block simply isn't part of the range.

CAVEAT2: The bytecode generated is efficient enough to only have the finally body once per catch block.

Hence, you have many different bytecode in your class file that all has the same 'line number'. Debugging fundamentally occurs in bytecode (e.g. when you set a breakpoint, you pick a line. However, some system needs to translate that line to an actual bytecode, and breakpoint that bytecode. In this case, that's tricky: The one line you breakpoint actually has 3 completely different bytecode items. Presumably, your debugger adds breakpoint hooks to all of them.

Just guessing, but it's easy to say how this leads to accidental double firing. It shouldn't - your debugger is clearly itself buggy (heh), but if you want to figure out why its happening, or even help out and write a PR to fix the problem, this is where I'd start.

You can check this 'whaaaa? Are finally blocks duplicated this much? Crazy!' stuff by using javap -c, which shows you bytecode.

For example:

class Test { void foo() {
    try {
        System.out.println("A");
    } catch (NullPointerException e) {
        System.out.println("B");
    } finally {
        System.out.println("C");
    }
}}

Then javac Test.java; javap -c Test shows:

void foo();
    Code:
       0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #13                 // String A
       5: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      11: ldc           #21                 // String C
      13: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      16: goto          50
      19: astore_1
      20: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      23: ldc           #25                 // String B
      25: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      28: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      31: ldc           #21                 // String C
      33: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      36: goto          50
      39: astore_2
      40: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      43: ldc           #21                 // String C
      45: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      48: aload_2
      49: athrow
      50: return
    Exception table:
       from    to  target type
           0     8    19   Class java/lang/NullPointerException
           0     8    39   any
          19    28    39   any

breaking that down: You can easily see how C is loaded three times, but A and B only once, which is bizarre given that C is only mentioned once in the source code - until you realize that finally blocks are duplicated all over the place.

冷夜 2025-02-03 13:23:24

如果您通过说明抛出的例外,则在方法中抛出异常,则您在方法中不会处理它。

实际上,您共享的代码有很多我无法显示我的示例的错误,因此我将显示我的示例。

可以说,您有一种称为foo()的方法,现在会引发异常

int foo(int a, int b) throws DivisionByZero{
    if (b == 0){
        DivisionByZero myException = new DivisionByZero();
        throw myException;
    }
    else
        return a/b;
}

,如果要在foo()方法中使用foo2()方法,那么您要么声明foo2()作为抛出 exception distibbyzero,或者您应该在foo2()方法中处理它。 不要

像这样的同时做:

foo2(int a, int b) throws DivisionByZero{
    int result = foo(a,b);
    System.out.println(result);
}

或以下:

foo2(int a, int b){
    int result;
    try{
        result = foo(a,b);
        System.out.println("No Exception.");
    }catch(DivisionByZero e){
        System.out.println("Caught.");
    }finally{
        System.out.println("I'm done."); //works once either completing try or catch.
    }        
    System.out.println(result);
}

该最后一个foo2()代码的输出既不

例外。

我受够了。

抓住。

我受够了。

0 //因为INT类型初始化为零。

If you throw an exception in a method by stating it throws the exception, then you do not handle it inside the method.

Actually the code you shared has lots of mistakes that I can't show my example, so I'll show my example.

Lets say you have a method called foo() that throws an exception

int foo(int a, int b) throws DivisionByZero{
    if (b == 0){
        DivisionByZero myException = new DivisionByZero();
        throw myException;
    }
    else
        return a/b;
}

Now, if you want to use foo() method in a foo2() method, then you either declare the foo2() as throws Exception DivisionByZero or you should handle it inside the foo2() method. DO NOT DO BOTH

Like this:

foo2(int a, int b) throws DivisionByZero{
    int result = foo(a,b);
    System.out.println(result);
}

or this:

foo2(int a, int b){
    int result;
    try{
        result = foo(a,b);
        System.out.println("No Exception.");
    }catch(DivisionByZero e){
        System.out.println("Caught.");
    }finally{
        System.out.println("I'm done."); //works once either completing try or catch.
    }        
    System.out.println(result);
}

output of this last foo2() code is either

No Exception.

I'm done.

or

Caught.

I'm done.

0 //because int type initialized to zero.

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