本文章为《深入浅出 Java 虚拟机》系列课程学习笔记,侵删。学习地址为 深入浅出 Java 虚拟机
class 文件
class 文件的结构如下:

- magic:魔数,用于标识当前 class 的文件格式,JVM 可据此判断该文件是否可以被解析,目前固定为 0xCAFEBABE
- major_version:主版本号
- minor_version:副版本号,这两个版本号用来标识编译时的 JDK 版本
- constant_pool_count:常量池计数器,等于常量池中的成员数加 1
- constant_pool:常量池,是一种表结构,包含 class 文件结构和子结构中引用的所有字符串常量,类或者接口名,字段名和其他常量
- access_flags:表示某个类或者接口的访问权限和属性
- this_class:类索引,该值必须是对常量池中某个常量的一个有效索引值,该索引处的成员必须是一个 CONSTANT_Class_info 类型的结构体,表示这个 class 文件所定义的类和接口
- super_class:父类索引
- interfaces_count:接口计数器,表示当前类或者接口直接继承接口的数量
- interfaces:接口表,是一个表结构,成员同 this_class,是对常量池中 CONSTANT_Class_info 类型的一个有效索引值
- fields_count:字段计数器,当前 class 文件所有字段的数量
- fields:字段表,是一个表结构,表中每个成员必须是 filed_info 数据结构,用于表示当前类或者接口的某个字段的完整描述,但它不包含从父类或者父接口继承的字段
- methods_count:方法计数器,表示当前类方法表的成员个数
- methods:方法表,是一个表结构,表中每个成员必须是 method_info 数据结构,用于表示当前类或者接口的某个方法的完整描述
- attributes_count:属性计数器,表示当前 class 文件 attributes 属性表的成员个数
- attributes:属性表,是一个表结构,表中每个成员必须是 attribute_info 数据结构,这里的属性是对 class 文件本身,方法或者字段的补充描述,比如 SourceFile 属性用于表示 class 文件的源代码文件名
方法调用
执行引擎想要运行某个对象的方法,需要先在栈上找到这个对象的引用,然后再通过对象的指针,找到相应的方法字节码。

关于方法的调用,Java 共提供了 5 个指令,来调用不同类型的函数:
- invokestatic 用来调用静态方法;
- invokevirtual 用于调用非私有实例方法,比如 public 和 protected,大多数方法调用属于这一种;
- invokeinterface 和上面这条指令类似,不过作用于接口类;
- invokespecial 用于调用私有实例方法、构造器及 super 关键字等;
- invokedynamic 用于调用动态方法
对于接口方法的调用,使用实例对象直接调用,和强制转化成接口调用,所调用的字节码指令分别是 invokevirtual 和 invokeinterface,它们是有所不同的。
另外还有一点,和我们想象中的不同,大多数普通方法调用,使用的是 invokevirtual 指令,它其实和 invokeinterface 是一类的,都属于虚方法调用。很多时候,JVM 需要根据调用者的动态类型,来确定调用的目标方法,这就是动态绑定的过程。
invokevirtual 指令有多态查找的机制,该指令运行时,解析过程如下:
- 找到操作数栈顶的第一个元素所指向的对象实际类型,记做 c;
- 如果在类型 c 中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法直接引用,查找过程结束,不通过则返回 java.lang.IllegalAccessError;
- 否则,按照继承关系从下往上依次对 c 的各个父类进行第二步的搜索和验证过程;
- 如果始终没找到合适的方法,则抛出 java.lang.AbstractMethodError 异常,这就是 Java 语言中方法重写的本质。
相对比,invokestatic 指令加上 invokespecial 指令,就属于静态绑定过程。所以静态绑定,指的是能够直接识别目标方法的情况,而动态绑定指的是需要在运行过程中根据调用者的类型来确定目标方法的情况。
可以想象,相对于静态绑定的方法调用来说,动态绑定的调用会更加耗时一些。由于方法的调用非常的频繁,JVM 对动态调用的代码进行了比较多的优化,比如使用方法表来加快对具体方法的寻址,以及使用更快的缓冲区来直接寻址( 内联缓存)。
本文介绍了Java类文件的基本结构,包括魔数、版本号、常量池等组成部分,并详细解释了方法调用过程中JVM如何根据类文件信息找到并执行对应的方法。
class 文件与方法调用&spm=1001.2101.3001.5002&articleId=108106993&d=1&t=3&u=8312bb803cb54101b6488183b6427398)
2万+

被折叠的 条评论
为什么被折叠?



