一、类加载过程
Java的类加载过程总共包含三大步,分别为:装载、链接和初始化;需要注意的是,这三个步骤并不是简单的串行,而有可能交叉并行的,下面详细介绍下这几个步骤。
1.1 装载
装载的任务主要有:
- 找到Java的字节码文件,将其二进制字节流加载到内存中。
- 将静态的数据结构转化为运行时数据结构并存入方法区。
- 创始化一个Class对象放入Java堆中,作为方法区数据的访问入口。
1.2 链接
链接可以细分为验证、准备和解析三个步骤。
1.2.1 验证
验证是确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全,主要包含以下四种验证方式:
-
文件格式验证:验证字节流是否符合Class文件格式的规范;例如:是否以 0xCAFEBABE开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。
-
元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求;例如:这个类是否有父类,除了 java.lang.Object之外。
-
字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
-
符号引用验证:确保解析动作能正确执行。
虽然验证功能很有必要,但是我们也可以通过设置 -Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。
1.2.2 准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。
- 此阶段处理的变量仅包括static修饰的类变量,不包括类的成员变量;类的成员变量是在初始化阶段,在堆区处理的。
- 若仅仅是static修饰,便只给它赋默认值,比如整型是0,对象是null,而不是我们显示设置的值。
- 若final和static同时修饰,会直接给它赋显示设置的值。
例如 public static int value=10:在此阶段被赋值为0,而public final static int value = 10,在此阶段被赋值为10。
1.2.3 解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。
-
符号引用就是一组符号来描述目标,可以是任何字面量。
-
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。
1.3 初始化
初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。
- 初始化时机
1.反射(Class.forName)
2.使用new关键字
3.其子类要进行初始化操作
4.操作了该类的静态成员:静态变量使用和赋值以及静态方法的使用
二、类加载器
2.1 类加载器类型
类加载器可以大致划分为以下三类:
- 启动类加载器: BootstrapClassLoader,负责加载存放在 JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被 -Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.开头的类均被 BootstrapClassLoader加载)。启动类加载器是无法被Java程序直接引用的。
- 扩展类加载器: ExtensionClassLoader,该加载器由 sun.misc.Launcher$ExtClassLoader实现,它负责加载 JDK\jre\lib\ext目录中,或者由 java.ext.dirs系统变量指定的路径中的所有类库(如javax.开头的类),开发者可以直接使用扩展类加载器。
- 应用程序类加载器: ApplicationClassLoader,该类加载器由 sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
2.2双亲委派机制
-
当 AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
-
当 ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
-
如果 BootStrapClassLoader加载失败(例如在 $JAVA_HOME/jre/lib里未查找到该class),会使用 ExtClassLoader来尝试加载;
-
若ExtClassLoader也加载失败,则会使用 AppClassLoader来加载,如果 AppClassLoader也加载失败,则会报出异常 ClassNotFoundException。
使用双亲委派机制的原因:
- 防止系统加载多份相同的class文件
- 保证程序的安全性,使系统的class文件不会被官方之外的类加载器加载
网友评论