
加载
类加载过程的第一步,主要完成3件事:
1.通过全类名获取定义此类的二进制字节流。
2.将字节流所代表的静态存储结构转换为方法区的运行时数据结构。
3.在内存中生成一个代表此类的 Class 对象,作为方法区这些数据的访问入口。
验证
这一阶段的目的确保 Class 文件的字节流中包含信息符合Java虚拟机规范的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身安全。
准备
正式为类变量分配内存并设置类变量初始值。该阶段需要注意以下几点:
1.类变量即静态变量,被 static 关键字修饰的变量,不包括实例变量。
2.类变量所使用的内存应当在方法区中进行分配。
解析
虚拟机将常量池内的符号引用替换为直接引用。举个例子:程序执行方法时,系统需要明确知道该方法所在位置。Java 虚拟机为每个类都准备了一张方法表来存放类中的所有方法。当需要调用一个类的方法时,只要知道这个方法在方法表中的偏移量就可以直接调用该方法了。通过解析操作符号引用就可以直接转变为目标方法在类中方法表的位置,从而使得方法可以被调用。
初始化
执行初始化方法 <clinit>() 方法的过程。<clinit>() 方法是带锁线程安全,在多线程环境下进行类初始化可能会引起多个线程阻塞。
初始化阶段,虚拟机严格规范了6种情况下,必须对类进行初始化:
1.当遇到 new、getstatic(即程序访问类的静态变量)、putstatic、invokestatic 四条字节码指令时,比如 new 一个类,读取一个静态字段(未被 final 修饰)、或者调用一个静态方法时。
(1)当 JVM 执行 new 指令时会初始化类。
(2)程序访问类的静态变量(不是静态变量会被加载到运行时常量池)。
(3)程序给类的静态变量赋值。
(4)程序调用类的静态方法。
2.使用 java.lang.reflect 包的方法对类进行反射调用时如 Class.forName(“…”),newInstance() 等。如果类没有初始化,需要触发其初始化。
3.初始化一个类,如果父类没有初始化,则会先触发该父类初始化。
4.虚拟机启动时,用户需要定义一个要执行的主类(包含 main 方法的类),虚拟机会先初始化这个类。
类卸载
卸载类需要满足3个要求:
1.该类的所有实例对象都已被GC,也就是说堆不存在该类的实例对象。
2.该类没有在其他任何地方被引用。
3.该类的类加载器的实例已被GC。
所以,在 JVM 生命周期内,由 JVM 自带的类加载器的类不会被卸载。但是由我们自定义的类加载器加载的类可能被卸载。
以上内容来自JavaGuid。