代码文件执行总体流程:
- 编译:将java源文件编译成class字节码文件
- 类加载:将class字节码文件装载进JVM
- 解释:JVM将class字节码文件解释成操作系统可以识别的指令
- 执行:操作系统执行指令
类加载过程:
- 装载:查找并加载类的二进制信息,在堆中生成一个
java.lang.Class对象,类的基本信息存放在方法区中 - 验证:验证类是否符合java规范和JVM规范
- 准备:为静态变量赋初始值,即默认值
- 解析:将符号引用转化为直接引用,可以无歧义地定位直接定位到虚拟机中的目标
- 初始化:为静态变量赋上正确的初始值,按照静态变量、静态代码块、静态方法、构造方法的顺序由上至下执行
1. 编译
作用:将java源码文件编译成class文件
操作内容:对源代码程序做语法分析、语义分析、注解处理(Lombok)、泛型擦除等
过程
- 词法分析:一个个字节地读取源代码,识别语言关键字,得到Token流
- 语法分析:检查Token流中关键字组合是否符合Java语法规范,形成抽象语法树(结构化的语法表达形式)
- 语义分析:将复杂语法转化为简单语法,如注解处理、泛型擦除、注释等,得到注解抽象语法树
- 字节码生成:根据注解抽象语法树生成符合Java虚拟机规范的字节码
2. 加载
作用:将class文件加载到JVM中
步骤:
-
装载
-
时机:有需要时才进行装载(new或反射),节省内存开销
-
操作:通过类加载器装载,防止内存中出现多份同样的字节码,使用双亲委派机制
不会自己尝试加载这个类,而是把请求委托给父加载器,依次向上
-
规则:本地方法由根加载器(BootstrapLoader)装载,内部实现的扩展类由扩展加载器(ExtClassLoader)装载,程序中的类由系统加载器(AppClassLoader)装载
-
内存角度:查找并加载类的二进制数据,在堆中创建一个java.lang.Class对象,类相关信息存储在方法区中
-
-
连接:对Class对象信息进行验证、为类变量分配内存空间并赋默认值
-
步骤:
-
验证:验证类是否符合Java规范和JVM规范
-
准备:为静态变量分配内存,初始化为默认值
-
解析:将符号引用转为直接引用
符号引用:可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可,与虚拟机布局无关
直接引用:与虚拟机布局相关,已经被加载到内存中,避免不同虚拟机对于相同符号引用翻译出的直接引用不同
-
-
-
初始化
- 为静态变量赋上正确的初始值
- 收集类中的静态变量、静态代码块、静态方法、构造方法,从上往下开始执行
3. 解释
作用:将class字节码文件转换为操作系统识别的指令
字节码解释器
- 非热点代码:直接解释
- 热点代码:编译,交给即时编译器
- 热点探测:方法调用计数器、回边计数器。达到阈值后,触发JIT编译
即时编译器JIT
- 保存热点代码的指令码,下次执行就无需重复解释,直接执行缓存的机器语言
4. 执行
操作系统执行指令码
5. 方法执行顺序
- 父类静态代码块
- 子类静态代码块
- 父类代码块
- 父类空参构造器
- 子类代码块
- 子类调用构造器
总结:静态 > 继承关系 > 代码块 > 构造器
默认子类构造器都会调用父类空参构造,如果父类无空参,子类必须说明使用的构造器
代码块早于构造器:提取各个构造器中公用部分,减少冗余