java类加载过程和双亲委派模式
java类加载的整体流程:通过双亲委派模式指定类加载器,然后通过类加载器进行加载
(一)指定类加载器
JDK默认ClassLoader
JDK 默认提供了如下几种ClassLoader
-
Bootstrp loader(启动类加载器)
底层是由C++实现的,实例是sun.misc.Launcher,在调用Launcher无参构造器的时候就是创建好扩展类和应用加载器 -
ExtClassLoader (标准扩展类加载器)
扩展类加载器是sun.misc.Launcher类中的静态内部类 -
AppClassLoader(应用程序类加载器)
应用加载器是sun.misc.Launcher类中的静态内不类

委派机制流程
首先classloader 分三个级别,最上级 : bootstrap classLoader 中间级:extension classLoader 最低级 app classLoader.
当需要加载某个类的时候,会看看这个类是否已经被加载了加载过的话直接返回,如果没有,会请求app 级来加载,app 请求 extension 级如果extension 加载过的话直接返回 没有加载过的话extension 请求 bootstrap级如果bootstrap加载过的话直接返回,没有加载过的话由最高级来负责加载(这个就是双亲委派,委托 上两级的loader来做加载),如果高级的无法加载 则会将人物返回给 下一级 以此类推 最后如果双亲都不行 就由自己来加载。
(二)类被类加载器加载的过程
1.加载
通过IO读取字节码文件(class文件)至JVM虚拟机方法区,同时在堆中创建class对象。
2.校验
验证一个Class的二进制内容是否合法,主要包括4个阶段:
- 文件格式验证,确保文件格式符合Class文件格式的规范。如:验证魔数、版本号等。
- 元数据验证,确保Class的语义描述符合Java的Class规范。如:该Class是否有父类、是否错误继承了final类、是否一个合法的抽象类等。
- 字节码验证,通过分析数据流和控制流,确保程序语义符合逻辑。如:验证类型转换是合法的。
- 符号引用验证,发生于符号引用转换为直接引用的时候(转换发生在解析阶段)。如:验证引用的类、成员变量、方法的是否可以被访问(IllegalAccessError),当前类是否存在相应的方法、成员等(NoSuchMethodError、NoSuchFieldError)。
3.准备
在准备阶段,虚拟机会在方法区中为Class分配内存,并设置static成员变量的初始值为默认值。注意这里仅仅会为static变量分配内存(static变量在方法区中),并且初始化static变量的值为其所属类型的默认值。如:int类型初始化为0,引用类型初始化为null。即使声明了这样一个static变量:
public static int a = 123;
在准备阶段后,a在内存中的值仍然是0, 赋值123这个操作会在中初始化阶段执行,因此在初始化阶段产生了对应的Class对象之后a的值才是123 ,用final static 修饰的变量(也就是常量)实在编译的时候就会分配。
4.解析
解析阶段,虚拟机会将常量池中的符号引用替换为直接引用,解析主要针对的是类、接口、方法、成员变量等符号引用。在转换成直接引用后,会触发校验阶段的符号引用验证,验证转换之后的直接引用是否能找到对应的类、方法、成员变量等。这里也可见类加载的各个阶段在实际过程中,可能是交错执行。
5.初始化
这个阶段主要是对类变量初始化,是执行类构造器的过程。
换句话说,只对static修饰的变量或语句进行初始化。
如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。
使用和卸载就不属于类加载器负责了
6.使用
7.卸载
当代表一个类的Class对象不再被引用,即不可触及时,Class对象就会结束生命周期,这个类在方法区内的数据也会被卸载,从而结束这个类的生命周期。
(1) 启动类加载器加载的类型在整个运行期间是不可能被卸载的(jvm和jls规范);
(2) 被系统类加载器和标准扩展类加载器加载的类型在运行期间不太可能被卸载,因为系统类加载器实例或者标准扩展类的实例基本上在整个运行期间总能直接或者间接的访问的到,其达到unreachable的可能性极小。(当然,在虚拟机快退出的时候可以,因为不管ClassLoader实例或者Class(java.lang.Class)实例也都是在堆中存在,同样遵循垃圾收集的规则);
(3) 被开发者自定义的类加载器实例加载的类型只有在很简单的上下文环境中才能被卸载,而且一般还要借助于强制调用虚拟机的垃圾收集功能才可以做到.可以预想,稍微复杂点的应用场景中(尤其很多时候,用户在开发自定义类加载器实例的时候采用缓存的策略以提高系统性能),被加载的类型在运行期间也是几乎不太可能被卸载的(至少卸载的时间是不确定的)
综合以上三点, 一个已经加载的类型被卸载的几率很小至少被卸载的时间是不确定的。同时我们可以看的出来,开发者在开发代码时候,不应该对虚拟机的类型卸载做任何假设的前提下来实现系统中的特定功能。
加载器和Class对象的关系:
在类加载器的内部实现中,用一个Java集合来存放所加载类的引用。
另一方面,一个Class对象总是会引用它的类加载器。调用Class对象的getClassLoader()方法,就能获得它的类加载器。
由此可见,Class实例和加载它的加载器之间为双向关联关系。
类、类的Class对象、类的实例对象:
一个类的实例总是引用代表这个类的Class对象。
在Object类中定义了getClass()方法,这个方法返回代表对象所属类的Class对象的引用。
此外,所有的Java类都有一个静态属性class,它引用代表这个类的Class对象。


浙公网安备 33010602011771号