类加载的过程

1,092 阅读3分钟

我正在参加「掘金·启航计划」

首先.class类文件是由我们编写的java文件也就是程序员写的代码编译过来的,编译之后就变成了class字节码文件,而字节码文件加载到jvm虚拟机里是由类加载子系统(Class Load SubSystem)完成的,类加载的过程由主要分为三步,第一步就是加载阶段(Loading)、第二步就是链接阶段(Linking)、第三步就是初始化阶(initialzation),如下图。

image.png

1. 加载阶段(Loading)

此加载阶段是狭义上的加载,并不是整个类加载的过程,广义的类加载是上述三步构成,加载阶段主要是由类加载子系统将class文件加载到内存当中,ClassLoadSubSystem只负责类的加载至于class是否能够运行由执行引擎决定。

2. 链接阶段(Linking)

加载完成之后就到了链接阶段,链接主要分为三步:

1.验证(verification):验证阶段主要是对class文件作校验,主要分为:文件格式校验、字节码校验、元数据验证、符号引用验证。保证类加载的正确性且执行class文件时不会对jvm本身造成危害。

比如class字节码文件开头是cafebabe,如下图:

image.png

2.准备(preparation) 准备阶段主要是为类变量初始化默认值比如int类型未赋值就是0,但是如果是final修饰的static类变量则不会在此阶段赋予默认值,因为final修饰的类变量为常量不会再变,在编译阶段就已经赋值,如果是实例变量也不会在此阶段初始化

3.解析(resolution) 将常量池内的符号引用转为直接引用的过程

image.png

3. 初始化阶(initialzation)

初始化阶段就是执行类的构造器方法(clinit)的过程,注意这里的clinit方法执行的不只是我们编写的类的构造方法(当然中间也包括)此方法是抽象的更准确的说是一个初始化的过程,此方法由javac编译器自动收集类中所有类变量和静态代码块的语句,无须定义,且构造器按照指令顺序执行赋值。

定义一个类常量num赋值为10,在static静态代码块里再次赋值为20,可以在字节码文件看到类常量赋值的情况,先是赋值10在赋值20,最终输出20,如下图: image.png

如果类有父类那么会优先执行父类的clinit方法才会执行子类的clinit方法进行初始化

image.png

虚拟机保证了一个类的clinit初始化方法是同步阻塞的(也就是加锁)保证了一个类只能执行clinit方法一次

image.png

如上图,开启两个线程,可以发现即使让主线程挂起20s也还是只有一次执行static静态代码块