Java 如何在低(程序集)级别捕获和处理异常?

Java 如何在低(程序集)级别捕获和处理异常?,java,code-generation,jit,Java,Code Generation,Jit,我有这个密码- try { doSomething(); } catch (Exception e) { e.printStackTrace(); } 编译器将如何实际实现这一点。在生成的汇编代码中,对异常的检查实际放在哪里 更新 我知道上面的代码是如何翻译成字节码的。字节码只将try-catch转换为相应的try处理程序块。我对如何将其转换为汇编和/或由jvm处理感兴趣。如果我正确理解了您的问题,下面的代码 public class Example { public

我有这个密码-

try {
     doSomething();
} catch (Exception e) {
   e.printStackTrace();
}
编译器将如何实际实现这一点。在生成的汇编代码中,对异常的检查实际放在哪里

更新

我知道上面的代码是如何翻译成字节码的。字节码只将try-catch转换为相应的try处理程序块。我对如何将其转换为汇编和/或由jvm处理感兴趣。

如果我正确理解了您的问题,下面的代码

public class Example {
    public static void main(String[] args) {
        try {
            otherMethod();
        }
        catch (Exception e) {}
        try {
            otherMethod();
            someMethod();
        }
        catch (SQLException e) {}
        catch (IOException e) {}
    }

    public static void someMethod() throws IOException {throw new IOException();}
    public static void otherMethod() throws SQLException, IOException {}
}
生成以下字节码(人类可读版本的摘录)

//  main method
     0: invokestatic  #2                  // Method otherMethod:()V
     3: goto          7
     6: astore_1      
     7: invokestatic  #2                  // Method otherMethod:()V
    10: invokestatic  #4                  // Method someMethod:()V
    13: goto          21
    16: astore_1      
    17: goto          21
    20: astore_1      
    21: return        
  Exception table:
     from    to  target type
         0     3     6   Class java/lang/Exception
         7    13    16   Class java/sql/SQLException
         7    13    20   Class java/io/IOException
您将注意到
异常表
。此结构指示VM,如果指令从
之间发生
类型的异常,则必须
转到
指令(偏移量)
目标
。它还指示它在堆栈上推送
异常
引用,以便可以复制其值并将其绑定到
捕获
块中的参数

您还可以看到与上面的
throw
语句相关的这篇文章

// someMethod method
     0: new           #6                  // class java/io/IOException
     3: dup           
     4: invokespecial #7                  // Method java/io/IOException."<init>":()V
     7: athrow        
//someMethod
0:new#6//类java/io/IOException
3:dup
4:invokespecial#7//方法java/io/IOException。”“:()V
7:athrow

抛出错误或异常(请注意,堆栈的其余部分 清除,只留下对可丢弃文件的引用)

objectref必须是reference类型,并且必须引用对象 这是Throwable类或Throwable子类的实例。 它从操作数堆栈中弹出。objectref然后由 在当前方法(§2.6)中搜索第一个异常处理程序 与objectref类匹配,如中的算法所示 §2.10

如果找到与objectref匹配的异常处理程序,则它包含 用于处理此异常的代码的位置。个人电脑 寄存器被重置到该位置,即当前寄存器的操作数堆栈 帧被清除,objectref被推回操作数堆栈,并且 继续执行

如果在当前帧中未找到匹配的异常处理程序,则 框架弹出。如果当前框架表示调用 同步方法,在调用时输入或重新输入监视器 该方法通过执行monitorexit指令退出 (§monitorexit)最后,如果 存在这样的帧,并且objectref将被重新调用。如果没有这样的帧 存在,则当前线程退出

因此,堆栈帧不断弹出,直到找到一个可以处理抛出的异常

编译器将如何实际实现这一点。火车在哪里 检查生成的汇编代码中实际存在的异常


编译器生成上面的字节码。没有检查异常,只有字节码指令。athrow的
athrow
将指示VM执行我们称之为抛出异常的任务,这将导致弹出堆栈,在当前堆栈帧中搜索异常表,等等。

我没有一个明确的答案给你,但我将为你提供获取程序集的步骤,你可以根据你的用例对它进行剖析

  • 确保您感兴趣的方法已编译为程序集
    • 我通常对循环使用2来破坏转义分析,并确保我的代码没有标记为NOP
  • 确保您正在运行调试JVM构建或已构建热点反汇编程序-
  • 使用java-XX:+UnlockDiagnosticVMOptions-XX:+PrintAssembly运行程序
    • 准备好看一些
还有一个GUI工具,用于分析和可视化JIT编译器的日志文件,称为

这是我用来测试这个的类,可能有点冗长,但是我得到了myMethod和doSomething来编译成汇编

public class Question {
  public static void main(String[] args) {
    long result = 0;
    for (int i = 0; i < 100; i++) {
      for (int j = 0; j < 100; j++) {
        result += myMethod();
      }
    }
    System.out.println(result);
  }

  private static long myMethod() {
    try {
      return doSomething();
    }
    catch (Exception e) {
      return 100;
    }
  }

  private static long doSomething() {
    if (System.currentTimeMillis() % 2 == 0)
      return System.currentTimeMillis();
    else
      throw new RuntimeException();
  }
}
公开课问题{
公共静态void main(字符串[]args){
长结果=0;
对于(int i=0;i<100;i++){
对于(int j=0;j<100;j++){
结果+=myMethod();
}
}
系统输出打印项次(结果);
}
私有静态长myMethod(){
试一试{
返回doSomething();
}
捕获(例外e){
返回100;
}
}
私人静态长剂量测量法(){
如果(System.currentTimeMillis()%2==0)
返回系统.currentTimeMillis();
其他的
抛出新的RuntimeException();
}
}

尝试捕获块的成本

粗略地说,
try
block不会向结果程序集添加任何异常检查代码。只要没有抛出异常,它基本上是一个no-op。所有缓慢的工作都是由异常抛出代码完成的

JIT编译
try catch
时,会在代码之外添加一个异常表。它将可能发生已处理异常的地址范围映射到相应异常处理程序的地址。注意:这些不是字节码索引,而是实际内存地址

如何在热点中引发异常?

  • 隐式异常:
    NullPointerException
    StackOverflowerError
    在信号处理程序中检测到,以响应分段错误
  • ArrayIndexOutOfBoundsException
    ClassCastException
    等都被显式选中。相应的检查被内联到完成数组访问的编译代码中
  • 无论何时执行线程状态转换(vm->java或native->java),都会显式检查从本机代码引发的所有其他异常
  • athrow
    bytecod引发的所有用户异常