当用java命令运行某个main函数时,首先需要类加载器把主类加载到JVM内存中。
下面就是一个class文件大致的加载流程:
如上图 将编译好的字节码class文件通过java命令,在win操作系统就是一个java.exe文件,这个文件底层是c++语言实现的,通过这个文件调用底层jvm.dll文件创建Java虚拟机,这个jvm.dll文件也是c++语言实现的就是一些类库。在创建JVM虚拟机的过程中会通过c++语言创建一个引导类加载器。最终由C++语言调用java代码创建JVM启动器Launcher,该类由引导类加载器创建其他类加载器,最终调用的Launcher类的getLauncher()方法,这个方法创建加载了扩展类加载器,应用程序类加载器。最终调用loadClass()方法实现类的加载。
其中loadClass的类加载过程有如下几步:
加载
在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
验证
校验字节码文件的正确性,不会危害虚拟机自身的安全
准备
给类的静态变量分配内存,并赋予默认值
数据类型
零值
int
0
long
0L
short
(short)0
char
'u0000'
byte
(byte)0
boolean
false
float
0.0f
double
0.0d
reference
null
解析
将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如
main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过
程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用
初始化
对于初始化阶段,虚拟机严格规范了有且只有5种情况下,必须对类进行初始化(只有主动去使用类才会初始化类):
当遇到getstatic、putstatic或invokestatic 这4条直接码指令时,比如读取一个静态字段(未被 final 修饰)、或调用一个类的静态方法时。
使用new关键字实例对象。
使用 java.lang.reflect 包的方法对类进行反射调用时如Class.forName(""),newInstance()等等。如果类没初始化,需要触发其初始化。
初始化一个类,如果其父类还未初始化,则先触发该父类的初始化。
当虚拟机启动时,用户需要定义一个要执行的主类 (包含 main 方法的那个类),虚拟机会先初始化这个类。
当一个接口中定义了JDK8新加入的默认方法(被default关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。
卸载
卸载类需要满足3个要求:
该类的所有的实例对象都已被GC,也就是说堆不存在该类的实例对象。
该类没有在其他任何地方被引用
该类的类加载器的实例已被GC