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

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. 执行对应方法。

相关文章:

  • spss statistics 在mac上安装
  • 【leetcode hot 100 131】分割回文串
  • Docker 安装 Mysql
  • Ubuntu 20.04 安装并使用Cursor
  • go语言中的strings库
  • 蓝桥杯 R格式
  • Redis常见阻塞原因总结
  • 【JavaEE进阶】Linux搭建Java部署环境
  • 用Ollama部署大语言模型
  • ABAP隐藏F8执行按钮
  • Kafka是如何实现幂等性的??
  • redis安装
  • STM32__红外避障模块的使用
  • c++进阶之------红黑树
  • [特殊字符] 树莓派声卡驱动原理全解析:从模拟耳机口到HiFi DAC
  • 《鸿蒙原生应用开发:掌控Ability生命周期的艺术》
  • Chats-Grid迭代检索问答优化方案
  • 基于Python+Django的旅游管理系统
  • 学习笔记--基于Sa-Token 实现Java项目单点登录+同端互斥检测
  • #13【CVPR2024】“不确定性不是敌人”:深入剖析多模态融合中的不确定性
  • 杭州发布最新“独角兽企业”榜单,“六小龙”中5家已晋级
  • 央行上海总部:上海个人住房贷款需求回升,增速连续半年回升
  • 封江晚开江早,东北地区主要江河上一冰封期冰层较常年偏薄
  • 世卫发布预防少女怀孕新指南,呼吁终止童婚、延长女孩受教育时间
  • 中国牵头制定,在线旅游机构和展览与活动领域ISO国际标准发布
  • 长征十梦舟揽月稳步推进