类加载器
简介
类加载器主要作用是将二进制字节码文件加载到JVM中,因为JVM只会运行二进制文件。目前类加载器一般都是java.lang.ClassLoader的子类,该类的主要职责是找到对应的类或生成对应的二进制字节码文件,同时加载程序所需的资源。
类加载器类别
类加载器包含以下4种:
1、启动类加载器(Bootstrap ClassLoader):此加载器不继承ClassLoader,用C++编写,用于加载JAVA_HOME/jre/lib目录下的类库。
2、扩展类加载器(Extension ClassLoader):此加载器继承ClassLoader,主要加载JAVA_HOME/jre/lib/exe目录中的类和库。
3、应用类加载器(Application ClassLoader):此加载器继承ClassLoader,主要加载classPath下的内容,即Java开发者自己编写的内容,我们编写的任何类都是由应用类加载器加载的,除非显式使用自定义类加载器。
4、自定义加载器(User-Defined ClassLoader):开发者在继承ClassLoader的基础上,实现自定义类的加载过程,这种类加载器通常用于加载网络上的类、执行热部署或为了安全目的自定义类的加载方式。
这4种类加载器的层次关系如下图:
双亲委派机制
当一个类加载器接收到类加载请求时,一般不会自己加载,而是将加载请求发送给父加载类,逐层递归,如果父加载类能完成加载,则加载成功;当父加载类无法完成加载时,才会由子加载类进行加载。
双亲委派机制的作用主要有:
1、可以避免某一个类被重复加载,当父类已经加载后则无需重复加载,保证唯一性。
2、保证Java核心库的安全性,保证其不被修改。如在String类中,创建main函数,此时就会报错,因为String属于java.lang.String包,根据双亲委派机制其需要启动类加载器加载,而在核心jre库中有对应的类,但类中无main函数,这样就能防止核心库被修改。
类加载的过程
1、加载
(1)使用类的全名,获取二进制数据流;
(2)将类的二进制数据流,解析为方法内的数据结构;
(3)在内存中生成java.lang.Class对象,作为方法区访问这个类的各种数据的入口。
2、验证
验证类是否符合JVM规范:
(1)文件格式验证:验证文件格式是否符合Class文件规范;
(2)元数据验证:验证这个类是否有父类(除Object外都有父类)、是否继承了被final修饰过的类(这些类无法被继承)、类中的字段和方法是否与父类冲突(被final修饰过的字段和方法不能被覆盖);
(3)字节码验证:通过字节码验证,主要完成对控制流和数据流的分析,来确保程序语义是合法、符合逻辑的;
(4)符号引用验证:符号引用验证可以看做是对类自身以外(主要是常量池中的各种符号引用)的信息进行匹配性校验,目的是确保后面进入解析阶段后,解析动作能够正常执行。符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量。如int i = 4,i为符号引用,4为字面量。
3、准备
为类变量分配内存并设置变量初始值。
(1)对于static类型的变量,此阶段分配内存空间并赋默认值;
(2)对于final修饰的static类的基本类型、字符串常量,值已确定,赋值在此阶段完成。
4、解析
将类中的符号引用转化为直接引用,如方法中调用了其他方法,方法名可以理解为符号引用,而直接引用就是使用指针直接指向方法。
5、初始化
(1)若一个类初始化时,其父类未被初始化,则先初始化其父类;
(2)若类中有多个静态变量、静态代码块,则从上到下完成对其初始化;
(3)对于类变量,被赋其期望的值。
6、使用
JVM 开始从入口方法开始执行程序代码,调用静态类成员信息(如静态字段、静态方法)
,使用new关键字为其创建对象实例。
7、卸载
当用户程序代码执行完毕后,JVM 便开始销毁创建的 Class 对象,最后负责运行的JVM 也退出内存。
其中验证、解析、准备统称连接阶段。