JVM详解【五】JVM的类加载机制
JVM的类加载机制Java类加载机制的定义把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。在Java语言里,类型的加载、连接和初始化过程都是在程序运行期间完成的,这种策略虽然会令类加载时稍微增加一些性能开销,但是会为Java应用程序提供高度的灵活性,Java里天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特
JVM的类加载机制
Java类加载机制的定义
把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。在Java语言里,类型的加载、连接和初始化过程都是在程序运行期间完成的,这种策略虽然会令类加载时稍微增加一些性能开销,但是会为Java应用程序提供高度的灵活性,Java里天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特点来实现的。
JVM的类加载阶段
JVM的类加载分为5个阶段:加载、验证、准备、解析和初始化。在类初始化完成后可以使用该类的信息,在一个类不再被需要时可以从JVM中卸载,如图所示:
1.加载
指JVM读取Class文件,并根据Class文件描述创建Java.lang.Class对象的过程。类加载过程主要包含将Class文件读取到运行时区域的方法区内,在堆中创建java.lang.Class对象,并封装在方法区内的数据结构的过程,在读取Class文件时既可以通过文件的形式读取,也可以通过jar包、war包读取,还可以通过代理自动生成Class或其他方式读取。
2.验证
主要用于确保Class文件符合当前虚拟机的要求,保障虚拟哦自身的安全,
只有通过验证的Class文件才能被JVM加载。
3.准备
主要工作是在方法区为类变量分配内存空间并设置类中的变量的初始值。初始值指不同数据类型的默认值,这里需要注意final类型的变量和非final类型的变量在准备阶段的数据初始化过程的不同。比如一个成员变量的定义如下:
public static long value = 1000;
在以上代码中,静态变量value在准备阶段的初始值为0,将value设置为1000的动作是在对象初始化时完成的,因为JVM在编译阶段会将静态变量的初始化操作定义在构造器中。但是,如果将变量value声明为final类型
public static final int value = 1000;
则JVM在编译阶段后会为final类型的变量value生成其对应的ConstantValue属性,虚拟机在准备阶段会根据ConstantValue属性将value赋值为1000.
4.解析
JVM会将常量池中的符号引用替换为直接引用。
5.初始化
主要通过执行类构造器的< clinit >方法为类进行初始化。< clinit >方法是在编译阶段由编译器自动收集类中静态语句块和变量的赋值操作组成的。JVM规定,只有在父类的< clinit >方法都执行成功后,子类中的< clinit >方法才可以被执行。在一个类中既没有静态变量赋值操作,也没有静态语句块时,编译器不会为该类生成< clinit >方法。
在发生一下几种情况时,JVM不会执行类的初始化流程。
- 常量在编译时会将其常量值存入使用该常量的类的常量池中,该过程不需要调用常量所在的类,因此不会触发该常量的初始化。
- 在子类引用父类的静态字符段时,不会触发子类的初始化,只会触发父类的初始化。
- 定义对象数组,不会触发该类的初始化。
- 在使用类名获取Class对象时不会触发类的初始化。
- 在使用Class.forName加载指定类时,可以通过initialize参数设置是否需要对类进行初始化。
- 在使用ClassLoader默认的loadClass方法加载类时不会触发该类的初始化。
类加载器
JVM提供了3种类加载器,分别是启动类加载器、扩展类加载器和应用程序类加载器,如图所示:
(1)启动类加载器:负责加载Java_HOME/lib目录中的类库,或通过-Xbootclasspath参数指定路径中被虚拟机认可的类库。
(2)扩展类加载器:负责加载 Java_HMOE/lib/ext 目录中的类库,或者通过java.ext.dirs系统变量加载指定路径中的类库。
(3)应用程序类加载器:负责加载用户路径(classpath)上的类库。
除了上面三种类加载器,我们也可以通过继承java.lang.ClassLoader实现自定义的类加载器。
双亲委派机制
JVM通过双亲委派机制对类进行加载。双亲委派机制指一个类在收到类加载请求后不会尝试自己加载这个类,二十八该类加载请求向上委派给其父类去完成,其父类在接收到该类的加载请求后优惠将其委派给自己的父类,以此类推,这样所有的类加载请求都被向上委派到启动类加载器中。若父类加载器在接收到类加载请求后发现自己也无法加载该类(通常原因时该类的Class文件在父类的类加载器中路径不存在),则父类会将该信息反馈给子类并向下委派子类加载器加载父类,直到该类被成功加载,若找不到该类,则JVM会抛出ClassNotFound异常。
双亲委派类加载机制的类加载流程如下,如图所示:
(1)将自定义加载器挂载到应用程序类加载器。
(2)应用程序类加载器将类加载器请求委托给扩展类加载器。
(3)扩展类加载器将类加载请求委托给启动类加载器。
(4)启动类加载器在加载路径下查找并加载Class文件。如果找不到目标Class文件,则交由扩展类加载器加载。
(5)扩展类加载器在加载路径下查找并加载Class文件,如果找不到目标Class文件,则交由应用程序类加载器加载。
(6)应用程序类加载器在加载路径下查找并加载Class文件,如果未找到目标Class文件,则交由自定义类加载器加载。
(7)在自定义加载器下查找并加载用户指定目录下的Class文件,如果在自定义加载路径下未找到目标Class文件,则抛出ClassNotFound异常。
双亲委派机制的核心时保障类加载器的唯一性和安全性。例如在加载rt.jar包中的java.lang.Object类时,无论那个类加载器加载这个类,最终都将类加载请求委托给启动类加载器加载,这样就保证了类加载的唯一性。如果在JVM中存在包名和类名相同的两个类,则该类无法被加载,JVM也无法完成类加载流程。
更多推荐
所有评论(0)