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引发的所有用户异常