一.全过程分析:
编译>加载>验证>准备>解析>初始化
二.详细流程如图:
三.各步骤介绍
1.编译(Compile)
编译器器将.java源文件转为供虚拟机理解的代码,该代码文件叫字节码文件(.class文件)
编译过程分为3个过程:分析和输入到符号表,注解处理,语义分析并生成.class文件
2.加载(Loading)
①加载过程描述:
1>通过类的全限定类名获取此类的二进制字节流
2>JVM通过类加载器(ClassLoader)将字节流加载进入内存
3>JVM将运行时数据区的方法区中划分出一块内存
4>将字节流代表的静态存储结构转化为方法区内的运行时数据
5>JVM在堆区中通过反射技术将方法区存储的类的结构全部反射再封装成java.lang.Class对象
****通过Class对象可以构造一个类的对象,同时对外提供访问其内部数据结构的接口
②类加载器的三种分类:
1>Bootstrap ClassLoader:
2>Extension ClassLoader
3>App ClassLoader
****类加载器在成功加载某个类之后,会把得到的java.lang.Class类的实例缓存起来
****下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,不会对其进行再次加载
3.连接(Linking)
①验证(Verification)
确保加载的class文件的字节流信息符合当前虚拟机的要求.由于加载java类生成class文件的不一定是JDK的JVM,所以有些class文件字节流有害,可能会导致系统崩溃,所以这里需要JVM根据规范进行验证.
经过的验证规范阶段如下:
1>文件格式验证:
确保字节流能被正确的存储于运行时数据区的方法区中
2>元数据验证:
对元数据信息进行语义进行校验.(是否实现了接口方法,重载方法对不对,此类是否有除Object外的父类)
3>字节码验证:
通过数据流和控制流分析,确定程序语义是合法且符合逻辑的.(方法体校验,栈内各指向和指令都正确)
4>符号引用验证:
在JVM将符号引用转化为直接引用时发生.它将对类自身以外的信息进行匹配性校验.(是否可以通过全限定类名找到该类,是否访问了private限定的属性,是否访问了不存在的方法)
②准备(Preparation)
为方法区中的各类变量设置初始值(非实例变量,实例变量在初始化阶段才会被赋值)和内存空间
1>静态变量:
public static int value = 10;
在准备阶段时会为value设置初始值0,但是在对象在初始化阶段才被赋值为10
2>static final修饰的变量
public static final int value = 10;
在准备阶段时value会被设置值为10
③解析(Resolution)
JVM将常量池中的符号引用替换为直接引用的过程.
解析类型:类或接口,字段,类方法,接口方法,方法类型,方法句柄,调用点限定符
4.初始化(Initialization)
执行类构造器<clinit>()的过程,其执行特点如下:
①<clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的(其收集顺序由在源文件中出现书序决定)
②<clinit>()与实例构造器<init>()不同,它不需要显示调用父类的类构造器,JVM会保证子类在执行类构造器之前,父类类构造器已经执行完毕.
③由于父类的<clinit>()优先执行,所以父类中定义的静态代码块优先执行
④若一个类中没有对类变量的赋值操作且没有静态代码块,编译器可以不为该类生成<clinit>()
⑤JVM保证多线程的情况下只有一个线程执行<clinit>(),且可以正确加锁和同步
⑥执行接口或者实现类的<clinit>()方法,不需要先执行父接口的<clinit>()方法,只有当父接口中定义的变量被使用,父接口才会初始化