今天我们来了解一下关于类的加载过程,这个问题在面试过程中属于高频面试题了。
那么回答的时候,我们往往会采取死记硬背的方式,告诉面试官类的加载过程包括:加载
、验证
、准备
、解析
和初始化
这5个阶段。
但是如果面试官继续问到,这5个步骤都具体做什么呢? 我们就回答不上来了。
那么今天,我们就通过和好朋友们一起去看电影的例子,来再次理解类的加载过程。
今天是一个阳光明媚的周末,你和几个关系很好的同事们一同相约去看《变形金刚》这部电影,由于这部电影上市以来异常火爆,所以电影院外面就挤满了人,为了保持安全和观影顺序,工作人员把大家安排成一排排的队列,依次的进入到电影院。
当我们通过编写java源码然后将其编译为class文件的时候,需要JVM将class文件加载进来才能执行我们所编写的程序逻辑,就正如上面故事所述的——“大家依次的进入到电影院
”中。其实也类似于我们要玩某款主机游戏,我们会将游戏光片放入到PS5的光驱中让其执行光盘的读取操作。
具体流程是,JVM通过类的全路径名称,获取类的二进制数据流。然后,解析类的二进制数据流,转化为方法区(永久代or元空间) 内部的数据结构。并且,创建java.lang.Class类的实例对象,用来表示该类型。
随着大家陆续的都进入到了电影院,那么为了保障观影安全,安保人员会对每一名来看电影的观众进行安全检查,比如:是否携带了易燃易爆的东西。
验证阶段的目从它的名字中就可以看得出来了,就是为了保证第一步中加载的字节码是合法且符合规范的。
这个过程大致可以分为4种
验证内容:
【格式检查】检查魔数、版本、长度等等。 【语义检查】抽象方法是否有实现类、是否继承了final类等等编码语义上的错误检查。 【字节码验证】跳转指令是否指向正确的位置,操作数类型是否合理等。 【符号引用验证】符号引用的直接引用是否存在
你和观影的小伙伴们都顺利的通过了安全检查,那么由于这部《变形金刚》购票的时候选择的是3D电影,所以,门口的服务员小姐姐非常贴心的为您提供了观影所需要的3D眼镜,此时,大家都录取的来领眼镜,为观影前做最后的准备工作。
准备阶段是正式 为类变量分配内存 并设置类变量的 初始值 阶段,即:在方法区中
分配这些变量所使用的内存空间。
注意这里所说的初始值概念,比如一个类变量定义为:public static int v = 8080
; 实际上变量v
在准备阶段过后的初始值为0
而不是8080
,将v
赋值为8080
的put static指令是程序被编译后
,存放于类构造器<client>方法之中。
但是注意,如果声明为:public static final
int v = 8080; 在编译阶段会为v生成ConstantValue属性,在准备阶段虚拟机会根据ConstantValue属性将v赋值为8080 。
领完3D眼镜,大家终于进入了电影院,根据电影票中写的x号厅x排x座,来寻找实际的座位,大家陆陆续续的都坐到了自己的座位上。
解析阶段是指虚拟机将运行时常量池中的符号引用替换为直接引用的过程。
那么,什么是符号引用?什么是直接引用呢?
符号引用指的是描述性的引用关系,比如上面故事中的“电影票中写的x号厅x排x座
”,它只进行了座位的描述,并不是实实在在的座位;
直接引用则对应真正的引用关系,即上面故事中电影票所对应的真实的座位
;
这一点,我们可以通过javap -verbose
指令来更直观的看到符号引用。
经过了上面的所有步骤,电影放映的时间到了,大家开开心心的欣赏这部全球大作《变形金刚》……
到达这个阶段,类就可以顺利加载到系统中。此时,类才会开始执行Java字节码。
初始化阶段是执行类构造器<client>方法的过程。<client>
方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证子<client>方法执行之前,父类的<client>方法已经执行完毕,如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成<client>()方法。
往期推荐