类的编译、加载、执行过程

菜鸟版


我们写了一个Book.java的类


编译:

执行javac test.java命令,就可以成功把Book.java转化成Book.class

编译有3步:

词法分析和输入到符号表
注解处理
语义分析和生成字节码

细化一点:

源代码文件*.java -> 词法分析器 -> tokens流 -> 语法分析器 -> 语法树/抽象语法树 -> 语义分析器 -> 注解抽象语法树 -> 字节码生成器 -> JVM字节码文件*.class

字节码文件包含:

结构信息,class文件相关信息
元数据,Java源码中的声明、常量信息

方法信息,Java源码语句、表达式对应


加载:

Book book = new Book();

当你new的时候,会用到Book.class,所以第一时间找到Book.class文件,并加载到内存中

执行该类的static代码块,给Person.class进行一个初始化

heap中开辟空间,分配地址

heap中建立对象的特有属性,并进行默认初始化

对属性进行显示初始化

对对象进行构造代码块初始化

对对象进行的与之对应的构造函数进行初始化

把内存地址赋给stack中的p变量


执行:

连接:

验证 验证类符合Java和JVM规范,和编译阶段的检查不同

准备 为类的静态变量分配内存,初始化为系统的初始值(不初始化静态代码块),对static final,直接定义为用户赋的值

解析 把符号引用(字面量描述)转为直接引用(对象和实例的地址指针、实例变量和方法的偏移量)

类初始化:

初始化类的静态变量和静态代码块为用户自定义的值。

非静态类在实例化类、在heap中创建对象的时候,才会初始化,初始化的顺序在源码中从上到下(如果还没有加载,则会顺势触发类的加载过程)。


内存分配:


JVM是基于栈结构的体系结构来执行class字节码的,不同于windows和Linux基于寄存器结构。类的执行机制,主要是在Java栈上面完成。当一个线程被创建后,Java栈和PC寄存器就会被创建。Java栈由栈帧组成,调用一个方法,就会生成一个栈帧(可以理解为表示调用一个方法)。栈帧又由局部变量表、操作数栈和常量池引用组成。
栈帧结构

执行的时候,每个线程都有一个Java栈,当前执行的栈称为当前栈。一个Java栈调用多个方法,则会push很多个栈帧,当前活动的栈帧称为当前栈帧。当前栈帧执行完毕之后,会把执行结果(如果有)压入到调用它的那个栈帧的操作数栈中,作为上一个栈帧的一个中间处理结果被调用,然后就会被pop出去。当所有调用的方法执行结束后,栈帧也就都pop掉没有了。
例如:执行代码
操作数栈执行过程演示

具体过程:

本步骤由执行引擎Execute Engine来完成。执行引擎把字节码转为机器码,然后操作系统才可以真正调用,在硬件环境上执行代码。执行引擎的通过Java字节码解释器(一行一行解释字节码)和JIT(Just In Time)即时编译器(对热代码整段编译)来完成机器码的翻译工作。JIT编译器的工作流程为:JVM字节码 -> 机器无关优化 -> 中间代码 -> 机器相关优化 -> 中间代码 -> 寄存器分配器 -> 中间代码 -> 目标机器码生成器 -> 目标机器码