JVM类的加载机制
1.编译期与执行期
在讲类的加载前,我们先了解一下一个java程序,从编译到运行经过了什么。
首先jdk里的编译器javac.exe会把java源程序编译成字节码,也就是.class文件。
然后jdk里的java.exe文件加载并执行字节码文件。
而类的加载过程,其实就是JVM虚拟机加载并执行字节码文件的过程,对应上图的右半部分的java程序执行期。
2.类的加载时机
那JVM什么时候会对类进行初始化呢?
JVM虚拟机规范严格规定了有且只有五种情况必须对类进行“初始化”,注意我们这里所讲的是类的初始化,不是类的对象初始化。
1) 遇到new、getstatic、putstatic、invokestatic这4条字节码指令时,如果在方法区类没有进行初始化(在JVM内存模型中类的初始
化信息存放在方法区),则需要先触发其初始化。生成这4条指令最常见的Java代码场景是:
* 使用new关键字实例化对象的时候
* 读取或设置一个类的静态字段时(被final修饰的静态字段除外)
* 调用一个类的静态方法时
2)使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先除法其初始化。
3) 当初始化一个类的时候,如果发现其父类没有初始化,则需要先触发其父类的初始化。
4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
5)当使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果
REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先出触发其
初始化。
这5种方式称为类的主动引用。初次之外,所有引用类的方式都不会触发其初始化,我们把不触发类的初始化引用称为被动引用。
3.类的加载过程
类的加载过程分为 - 加载-验证-准备-解析-初始化
而验证-准备-解析这三个过程由统称为链接过程,下面分别对这几个过程进行介绍。
2.1 加载
加载阶段做三件事情:
* 通过类的全限定名获取定义此类的二进制字节流。
* 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
* 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口。
简而言之,JVM或获得.class文件种关于类的二进制字节流,然后在方法区创建一个关于该类的Class对象(java.lang.Class类是反
射相关的知识,这里不做过多介绍),用来存储关于该类的信息(静态属性、静态方法、接口等),作为方法区访问该类数据的入
口。
2.2验证
验证主要包括文件格式验证、元数据验证、字节码验证、符号引用验证。
目的在于确保Class文件的字节流种包含的信息符合当前虚拟机的要求,保证被加载的类的正确性,不会危害虚拟机自身的安全。
2.3准备
* 为类变量分配内存并设置该变量的默认初始值,即零值。
这里不包含用final修饰的static,因为final在编译的时候就会分配,准备阶段会显式初始化。
准备阶段不会为实例初始化,类变量会分分配在方法区种,而实例变量是会随着对象一起分配到堆空间中。
2.4解析
将常量池内的符号引用转化为直接引用。
2.5初始化
初始化(在准备中先初始化为零值,在初始化时赋值为具体的值):静态代码块的执行和静态变量的显示赋值。
初始化过程就是执行类构造器方法<clinit>的过程。
此方法不需要定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。
虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁。
3.类加载器的分类
在介绍完了类的加载过程后,我们介绍类的加载器分类。
-
引导类加载器:JAVA核心类库都是使用引导类加载器进行加载。并不继承子java.lang.ClassLoader,没有父类加载器。加载扩展类和系统类加载器,并指定为它们的父类加载器。
-
扩展类加载器:从JDK的扩展目录下加载类库。父类是引导类加载器。
-
系统类加载器:父类加载器为扩展类加载器,用户自定义的类使用系统加载器进行加载。
4. 双亲委派机制
如果一个类的加载器收到了类加载请求,它并不自己先去加载,而是把这个请求委托给父类的加载器去执行。如果父类的加载器
还存在其父类加载器,则进一步向上委托给父类加载器,依次递归,请求最终将到达顶层的启动类加载器。如果父类加载器可以
完成类加载任务,就成功返回,倘若父类加载器无法完成此任务,子加载器才会尝试自己去加载,这就是双亲委派模式。
优点:
* 防止类被重复加载。
* 保护程序安全,防止核心API被篡改。
本文地址:https://blog.csdn.net/weixin_44094948/article/details/107889579
上一篇: Java中的四种引用
下一篇: Java的邮件发送功能实现