根据JVM规范,一个Java文件从被加载到被卸载的整个声明过程,总共要经历5个阶段:加载-> 链接(验证,准备,解析)->初始化->使用->卸载。因此也有一些典籍说Java的生命周期包含7个阶段
-
加载:
将Java类的字节码文件(1*)加载到机器内存中,并在内存中构建出Java类的原型-类模板对象(2*)。 所谓的类模板对象,其实就是Java类在JVM内存中的一个快照,JVM将从字节码文件中解析出的常量池,类字段,类方法等信息存储到类模板中,这样JVM在运行期便能够通过类模板而获取Java类的任意信息。反射就是基于这一基础(3*)。
补充1:任何的代码都需要通过编译工具来转换成对应的机器码来交给cpu执行,不同的cpu执行相同操作的指令不同,这就导致了同样的功能在不同的平台需要书写不同的代码。java的跨平台机制则是采用了一种中间语言的方法-字节码来实现跨平台。 我们书写同样的代码,在不同的平台会有不同的jdk来将其转换成不同的字节码,再由跨平台的jvm来将字节码转换成指定的机器码交由cpu执行,从而达到功能一致。对于java开发人员来说,只需要按照固定的格式来书写java代码,再根据平台来选择对应的jdk,该jdk会将其转成该平台适配的机器码来执行。
补充2:每一个Java类模型,最终在JVM内部都会有一个klassOop与之对等。 instanceKlass为java类模板对象,存储类的常量池,类字段、类方法。 instanceOop为该类具体的对象,存放在堆中。该对象也有一个指针指向instanceKlass 类模板对象来通过对象实现反射。
补充3:jdk实现的反射是通过klassOop,asm、cglib等字节码工具库类则是在运行期动态修改静态声明的java类所对应的字节码内容,从而在运行期直接修改掉java类的定义,甚至直接在运行期创建一个全新的Java类
-
链接
链接的作用是将字节码指令中对常量池中的索引引用转换为直接引用。链接包含三个步骤:验证、准备、解析。其实在类的加载阶段,JVM也会对字节码进行验证,只不过该阶段的验证着重于字节码文件格式本身,与该阶段的验证侧重点不同。在链接阶段,着重于由字节码信息出发进行反向验证。例如,验证根据字节码文件中的类名是否能找到对应的类模板、改类是否继承了final类。这些都验证无误后,JVM才能放心的加载当前类,也才能放心的将字节码指令中对常量池索引号的引用重写为直接引用。
-
初始化
所谓初始化,说白了就是调用java类的<clinit>()方法。该方法是编译器在编译期间自动生成的。当java类中出现静态字段或者包含statis{}块逻辑时,所编译出来的java字节码文件中便会自动包含一个名为<clinit>的方法。该方法不能由程序员调用,只能由jvm在运行期调用,这个调用的过程便是java类的初始化。
-
使用
使用分为主动使用和被动使用。 主动使用包括new一个类、调用类的静态方法、这个类是程序的入口类、对这个类进行反射的时候。主动引用一个类都会导致类的初始化
被动使用包括引用父类的静态字段、引用类的常量。
-
卸载
满足三个条件该文件会从jvm里卸载
1, java堆里不存在该类的实例
2,加载改类的ClassLoader被回收
3,该类对应的Class对象没有被任何地方引用