目录

类文件结构

Class类文件的结构

字节码指令简介

虚拟机类加载机制

类加载的时机

类加载器


类文件结构

Java曾有一个宣传口号:"一次编写,到处运行",字节码是构成平台无关性的基石。

Class类文件的结构

Java 类文件的结构如下:

1. 魔数(Magic Number):4 个字节,用于标识该文件是否为 Java 类文件,其值为 0xCAFEBABE。

2. 版本号(Version):2 个字节,分别表示 Java 类文件的版本号,其中第一个字节表示主版本号,第二个字节表示次版本号。

3. 常量池(Constant Pool):变长的表,用于存放编译期间生成的各种字面量和符号引用,包括类、方法和字段等的符号引用。

4. 访问标志(Access Flags):2 个字节,用于表示该类的访问标志,包括 public、final、abstract 等。

5. 类索引、父类索引与接口索引集合(This Class、Super Class and Interfaces):3 个字节,分别表示该类、父类和实现的接口的索引。

6. 字段表集合(Fields):变长的表,用于描述该类或接口声明的变量。

7. 方法表集合(Methods):变长的表,用于描述该类或接口声明的方法。

8. 属性表集合(Attributes):变长的表,用于描述该类或接口的附加信息,例如源文件名、注解等。

字节码指令简介

Java 字节码指令是一组基于栈的指令集,用于描述 Java 程序的操作。它是 Java 语言的中间表示形式,可以被 Java 虚拟机(JVM)解释执行。

Java 字节码指令可以分为以下几类:

1. 加载和存储指令:用于将数据从内存中加载到栈中或将栈中的数据存储到内存中。比如:aload是将一个引用类型从局部变量表加载到栈中,astore是将一个引用类型从栈中存储到局部变量表中。

2. 运算指令:用于对栈中的数据进行算术、逻辑和位运算等操作。比如:iadd是将栈顶的两个 int 类型数据相加,并将结果压入栈中。

3. 类型转换指令:用于将一个数据类型转换为另一个数据类型。

4. 控制转移指令:用于控制程序的执行流程,包括跳转、条件分支和异常处理等。比如:if_icmpne是假如栈顶的两个 int 类型数据不相等,则跳转到指定位置。

5. 方法调用和返回指令:用于调用方法和返回方法的结果。比如:invokevirtual是调用实例方法;invokestatic是调用静态方法。

6. 对象创建和操作指令:用于创建对象、调用对象方法和操作对象属性等。比如:new:创建一个新的对象;getfield是获取对象的字段值;putfield是设置对象的字段值。

源码:

void onlyMe(Foo f) {
     sychronized(f) {
        doSomething();
     }
}

字节码:

Method void onlyMe(Foo)
0 aload_1                                //将对象f入栈
1 dup                                    //复制栈顶元素(即f的引用)
2 astore_2                               //将栈顶元素存储到局部变量表Slot 2中
3 monitorenter                           //以栈顶元素(即f)作为锁,开始同步
4 aload_0                                //将局部变量Slot 0(即this指针)的元素入栈
5 invokevirtual #5                       //调用doSomething()方法
8 aload_2                                //将局部变量Slow 2的元素(即f)入栈
9 monitorexit                            //退出同步
10 goto 18                               //方法正常结束,跳转到18返回
13 astore_3                              //从这步开始是异常路径,见下面异常表的Taget 13
14 aload_2                               //将局部变量Slow 2的元素(即f)入栈
15 monitorexit                           //退出同步
16 aload_3                               //将局部变量Slow 3的元素(即异常对象)入栈
17 athrow                                //把异常对象重新抛出给onlyMe()方法的调用者
18 return                                //方法正常返回
Exception table:
FromTo Target Type
  4    10     13 any
 13    16     13 any

虚拟机类加载机制

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

类加载的时机

深入了解Java虚拟机之虚拟机执行子系统_jvm

 

1. 加载阶段(Loading):将类的字节码文件加载到内存中,并创建一个对应的 Class 对象。

2. 验证阶段(Verification):验证字节码文件的格式是否符合 Java 虚拟机规范。

3. 准备阶段(Preparation):为类的静态变量分配内存,并设置默认值。

4. 解析阶段(Resolution):将符号引用解析成直接引用。

5. 初始化阶段(Initialization):执行类的初始化代码,包括静态变量的赋值和静态代码块的执行等。

什么时候会触发加载呢?

Java 类的加载阶段是在 Java 程序运行时动态加载的,具体的加载时机为:

1. 当程序创建一个类的实例时,需要先加载该类的字节码文件,并创建对应的 Class 对象。

2. 当程序访问一个类的静态变量或静态方法时,需要先加载该类的字节码文件,并创建对应的 Class 对象。

3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

4. 当程序使用反射机制访问一个类时,需要先加载该类的字节码文件,并创建对应的 Class 对象。

5. 当程序启动时,需要加载程序的主类,并创建对应的 Class 对象。

类加载器

Java 类加载器是 Java 虚拟机(JVM)的一部分,负责将类的字节码文件加载到内存中,并创建对应的 Class 对象。Java 类加载器可以根据不同的加载策略和优先级,将类的字节码文件从不同的来源加载到内存中。

Java 类加载器可以分为以下几种类型:

1. 启动类加载器(Bootstrap ClassLoader):负责加载 Java 核心类库,如 java.lang 包中的类。启动类加载器是由 JVM 实现的,不是 Java 类,因此无法在程序中直接使用。

2. 扩展类加载器(Extension ClassLoader):负责加载 Java 扩展类库,如 java.ext 包中的类。

3. 应用程序类加载器(Application ClassLoader):也称为系统类加载器(System ClassLoader),负责加载应用程序中的类。

4. 自定义类加载器(Custom ClassLoader):可以根据需要自定义类加载器,实现自己的加载策略和优先级。自定义类加载器需要继承 java.lang.ClassLoader 类,并实现 findClass() 方法。

深入了解Java虚拟机之虚拟机执行子系统_类文件结构_02

 类加载器双亲委派模型

Java 类加载器采用双亲委派模型(Delegation Model),即当一个类需要被加载时,首先将该任务委派给父类加载器,只有当父类加载器无法加载该类时,才由当前类加载器自己来加载。这种委派模型可以保证类的唯一性和安全性,避免了同一类被多个类加载器重复加载的问题。