15方法调用与异常捕获原理

1.方法调用的原理

方法调用的本质是通过字节码指令的执行,能在栈上创建栈帧,并执行调用方法中的字节码执行。以invoke开头的字节码指令的作用是执行方法的调用。

1、调用study方法,会执行invokestatic指令,Java虚拟机找到#2对应的方法,也就是study方法,创建栈帧。

2、eat和sleep方法也是类似的处理方式。

3、方法栈帧创建之后,就可以执行方法里的字节码指令了。

在JVM中,一共有五个字节码指令可以执行方法调用:

1、invokestatic:调用静态方法

2、invokespecial: 调用对象的private方法、构造方法,以及使用 super 关键字调用父类实例的方法、构造方法,以及所实现接口的默认方法。

3、invokevirtual:调用对象的非private方法。

4、invokeinterface:调用接口对象的方法。

5、invokedynamic:用于调用动态方法,主要应用于lambda表达式中,机制极为复杂了解即可。

Invoke方法的核心作用就是找到字节码指令并执行。

Invoke指令执行时,需要找到方法区中instanceKlass中保存的方法相关的字节码信息。但是方法区中有很多类,每一个类又包含很多个方法,怎么精确地定位到方法的位置呢?

静态绑定

1、编译期间,invoke指令会携带一个参数符号引用,引用到常量池中的方法定义。方法定义中包含了类名 + 方法名 + 返回值 + 参数。

2、在方法第一次调用时,这些符号引用就会被替换成内存地址的直接引用,这种方式称之为静态绑定。

静态绑定适用于处理静态方法、私有方法、或者使用final修饰的方法,因为这些方法不能被继承之后重写。

invokestatic

invokespecial

final修饰的invokevirtual

动态绑定

对于非static、非private、非final的方法,有可能存在子类重写方法,那么就需要通过动态绑定来完成方法地址绑定的工作。比如在这段代码中,调用的其实是Cat类对象的eat方法,但是编译完之后虚拟机指令中调用的是Animal类的eat方法,这就需要在运行过程中通过动态绑定找到Cat类的eat方法,这样就实现了多态。

动态绑定是基于方法表来完成的,invokevirtual使用了虚方法表(vtable),invokeinterface使用了接口方法表(itable),整体思路类似。所以接下来使用invokevirtual和虚方法表来解释整个过程。

每个类中都有一个虚方法表,本质上它是一个数组,记录了方法的地址。子类方法表中包含父类方法表中的所有方法;子类如果重写了父类方法,则使用自己类中方法的地址进行替换。

产生invokevirtual调用时,先根据对象头中的类型指针找到方法区中InstanceClass对象,获得虚方法表。再根据虚方法表找到对应的对方,获得方法的地址,最后调用方法。

2.异常捕获的原理

在Java中,程序遇到异常时会向外抛出,此时可以使用try-catch捕获异常的方式将异常捕获并继续让程序按程序员设计好的方式运行。比如如下代码:在try代码块中如果抛出了Exception对象或者子类对象,则会进入catch分支。

异常捕获机制的实现,需要借助于编译时生成的异常表。

异常表在编译期生成,存放的是代码中异常的处理信息,包含了异常捕获的生效范围以及异常发生后跳转到的字节码指令位置。

起始/结束PC:此条异常捕获生效的字节码起始/结束位置。

跳转PC:异常捕获之后,跳转到的字节码位置。

在位置2到4字节码指令执行范围内,如果出现了Exception对象的异常或者子类对象异常,直接跳转到位置7的指令。也就是i = 2代码位置。

程序运行中触发异常时,Java 虚拟机会从上至下遍历异常表中的所有条目。当触发异常的字节码的索引值在某个异常表条目的监控范围内,Java 虚拟机会判断所抛出的异常和该条目想要捕获的异常是否匹配。

1、如果匹配,跳转到“跳转PC”对应的字节码位置。

2、如果遍历完都不能匹配,说明异常无法在当前方法执行时被捕获,此方法栈帧直接弹出,在上一层的栈帧中进行异常捕获的查询。

多个catch分支情况下,异常表会从上往下遍历,先捕获RuntimeException,如果捕获不了,再捕获Exception。

finally的处理方式就相对比较复杂一点了,分为以下几个步骤:

1、finally中的字节码指令会插入到try 和 catch代码块中,保证在try和catch执行之后一定会执行finally中的代码。

如下,在i=1i=2两段字节码指令之后,都加入了finally下的字节码指令。

2、如果抛出的异常范围超过了Exception,比如Error或者Throwable,此时也要执行finally,所以异常表中增加了两个条目。覆盖了try和catch两段字节码指令的范围,any代表可以捕获所有种类的异常。

14 条评论

  1. Harper1812 2025-11-30 19:31 回复
  2. Isabella1186 2025-12-02 10:22 回复
  3. Anderson2759 2025-12-05 04:35 回复
  4. Ivan2468 2025-12-17 15:20 回复
  5. Norman2333 2025-12-17 19:53 回复
  6. Jordyn4308 2025-12-28 01:37 回复
  7. Alissa6 2025-12-28 21:47 回复
  8. Eric2791 2025-12-29 01:19 回复
  9. Abigail3662 2026-01-01 21:28 回复
  10. Avery4947 2026-01-10 07:18 回复
  11. Tara1957 2026-01-12 18:55 回复

回复 Drake1320 取消回复

您的邮箱地址不会被公开。 必填项已用 * 标注