当前位置: 首页 > article >正文

Java 方法执行原理底层解析

java 文件经过javac编译后,变成了存储了一系列指令的.class文件。本文从指令层面分析Java 方法从解析、调用到执行的过程。

1 指令

一般格式:操作码 [操作数1] [操作数2] ...

操作码

1个字节的无符号整数(范围:0x00 ~ 0xFF)。

特点:1)每个操作码对应一个助记符(如iconst_1,iload,iadd等)。2)操作码决定了操作数类型及个数。

操作数

操作码所需的参数,紧跟在操作码后面。

表 指令的组成

1.1 未对齐的操作数

操作数的基本单位是字节。为了让.class文件更紧凑,jvm没有让操作数对齐。这意味着JVM需要逐个字节读取。

例如指令:0x11 0x03 0xE8 (sipush 1000)

0x11 是操作码,它的助记符是sipush。这个操作码的参数是1个2字节长度的操作数。

jvm 先读取第1个字节0x03,然后读取第2个字节 0xE8,最后将这两个字节组合:

(0x03 << 8) | 0xE8 => 0x03E8。

1.2 指令的执行模型

不考虑异常处理的话,JVM的解释器解析.class文件中的指令伪代码如下:

do {
  PC 寄存器值++;
  根据PC寄存器指示的位置,读取操作码;
  if (操作码需要操作数) 读取操作数;
  执行操作码所定义的操作;
} while(字节流长度 > 0);

1.3 方法相关指令

invokestatic

静态方法。

invokespecial

需要特殊处理的实例方法,包括实例初始化、私有方法、和super调用。

invokevirtual

虚方法分派(可被重写的方法及final方法)。

invokeinterface

接口方法,在运行期间再确定一个实现该接口的对象。

invokedynamic

先在运行时动态解析调用点限定符所引用的方法,然后再执行该方法。Java 7 引入,用于支持动态语言特性,比如Lambda

表 调用方法的指令

方法调用指令和数据类型无关,而方法返回指令根据返回值类型区分,包括ireturn、lreturn、freturn、dreturn、areturn(返回值为对象、数组等引用类型)及return(返回值为void)。

1.3.1 类与实例的初始化

<clinit>

类(或接口)的静态初始化方法。用于执行静态变量的赋值和静态代码块。

如果父类未初始化,会先触发父类的clinit方法,但接口的clinit不会因为实现类的初始化而触发(需要直接使用接口的静态变量才触发)。

<init>

对象的初始化方法,用于实例变量的赋值、实例代码块及构造器。

每个构造器对应一个init方法;子类构造器会隐式调用父类的init方法。

表 类与实例初始化方法

<clinit> 与<init>方法都是由编译器自动生成,用户无法调用。

2 方法调用

java 是一门静态多分派(和接收者及参数有关)、动态单分派(只能接收者有关)的语言。

静态分派:方法的静态类型在编译阶段是可知的(如方法重载,取决于参数类型、数量及位置)。

动态分派:运行时类型要在运行期才可知(方法重写,取决于执行对象的实际类型)。

2.1 虚方法表

JVM 在类初始化过程中,会为这个类维护一个虚拟方法表,存储该类所有可被重写的方法的入口地址。

虚方法表可理解为一个数组,每个数组元素(槽位)存储的是方法的入口地址。

虚方法表创建步骤如下

1)父类的方法按声明顺序占据虚方法表的固定槽位。

2)子类继承父类的虚方法表,并保留父类方法的地址。

3)如果子类重写了父类的方法,子类的虚方法中对应的槽位会被替换为子类方法的地址。

4)子类新增的方法追加到虚方法表的末尾。

例如 Animal类又两个可重写的方法sound和eat,Dog类继承Animal类,并重写了eat方法,又新增了一个方法wagTail。则这两个类的虚方法表如下。

Animal类的虚方法表

Dog类的虚方法表

索引

方法地址

索引

方法地址

0

Animal.sound()

0

Animal.sound()

1

Animal.eat()

1

Dog.eat()

2

Dog.wagTail()

表 Animal 与 Dog类的虚方法表

2.1.1 动态分派过程

当父类引用调用方法时,JVM执行步骤如下:

  1. 获取对象的实际类型。
  2. 根据对象的实际类型查找对应的虚方法表。
  3. 根据方法调用指令的操作数(虚方法表的索引),找到方法入口地址。
  4. 执行对应方法。

http://www.kler.cn/a/597695.html

相关文章:

  • HTML——什么是块级元素,什么是内联元素,有何区别
  • 高端网站设计:艺术与科技的完美融合,引领数字新风尚
  • 【人工智能-前端OpenWebUI】--图表显示
  • python:调用 ui2 获取当前页面所有实时文本
  • 数据结构-----树
  • OAK相机入门(四):近距离深度图
  • 2025_0321_生活记录
  • Winform在工控行业对比Wpf的优势?
  • 双核锁步技术在汽车芯片软错误防护中的应用详解
  • 在大数据开发中ETL是指什么?
  • 如果没有负载均衡,普通路由器怎么实现叠加两条宽带的带宽?
  • 组合总数||| 电话号码的字母组合
  • Ranger 鉴权
  • 基于C语言实现的观察者模式 以温度监控系统为例
  • 图扑软件 2D 组态:工业组态与硬件监控的物联网赋能
  • Android Compose 线性布局(Row、Column)源码深度剖析(十)
  • Python学习第二十一天
  • Java 输入1~100的整数,当读入负数时结束,统计输出每个数的数量
  • Git Flow 分支管理策略
  • 运算符重载(关键字operator的使用)