IT评测·应用市场-qidao123.com

标题: JVM类加载过程详解 [打印本页]

作者: 圆咕噜咕噜    时间: 2025-3-24 07:05
标题: JVM类加载过程详解

前言

类从被加载到假造机内存中开始到卸载出内存为止,它的整个生命周期可以简朴概括为 7 个阶段:加载(Loading)、验证(Verification)、预备(Preparation)、剖析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)。其中,验证、预备和剖析这三个阶段可以统称为连接(Linking)。
这 7 个阶段的顺序如下图所示:

注意:体系加载 Class 类型的文件重要三步:加载->连接->初始化。连接过程又可分为三步:验证->预备->剖析
1.加载

类加载过程的第一步,重要完成下面 3 件事情:
加载这一步重要是通过我们背面要讲到的 类加载器 完成的。类加载器有许多种,当我们想要加载一个类的时候,具体是哪个类加载器加载由 双亲委派模型 决定。
每个 Java 类都有一个引用指向加载它的 ClassLoader。不外,数组类不是通过 ClassLoader 创建的,而是 JVM 在必要的时候自动创建的,数组类通过getClassLoader()方法获取 ClassLoader 的时候和该数组的元素类型的 ClassLoader 是一致的。
一个非数组类的加载阶段(加载阶段获取类的二进制字节流的动作)是可控性最强的阶段,这一步我们可以去完成还可以自定义类加载器去控制字节流的获取方式(重写一个类加载器的 loadClass() 方法)。
加载阶段与连接阶段的部分动作(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未竣事,连接阶段可能就已经开始了。
将类的字节码载入方法区中,内部采用 C++ 的 instanceKlass 描述 java 类,它的重要 field 有:

2.链接

验证

验证是连接阶段的第一步,这一阶段的目的是确保 Class 文件的字节流中包含的信息符合《Java 假造机规范》的全部约束要求,保证这些信息被看成代码运行后不会危害假造机自身的安全。
验证阶段这一步在整个类加载过程中泯灭的资源照旧相对较多的,但很有必要,可以有效防止恶意代码的执行。任何时候,程序安全都是第一位。
不外,验证阶段也不是必须要执行的阶段。可以在生产环境的实行阶段就可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,以缩短假造机类加载的时间。
但是必要注意的是 -Xverify:none 和 -noverify 在 JDK 13 中被标志为 deprecated ,在未来版本的 JDK 中可能会被移除。
验证阶段重要由四个检验阶段组成:
文件格式验证

文件格式验证这一阶段是基于该类的二进制字节流进行的,重要目的是保证输入的字节流能正确地剖析并存储于方法区之内,格式上符合描述一个 Java 类型信息的要求。
除了这一阶段之外,其余三个验证阶段都是基于方法区的存储结构上进行的,不会再直接读取、操作字节流了。
这一阶段可能包罗下面这些验证点:

注意:方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。当假造秘密使用一个类时,它必要读取并剖析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被假造机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据
元数据验证

第二阶段是对字节码描述的信息进行语义分析,以保证其描述的信息符合《Java语言规范》的要
求,这个阶段可能包罗的验证点如下:

字节码验证

第三阶段是整个验证过程中最复杂的一个阶段,重要目的是通过数据流分析和控制流分析,确定程序语义是正当的、符合逻辑的。
在第二阶段对元数据信息中的数据类型校验完毕以后,这阶段就要对类的方法体(Class文件中的Code属性)进行校验分析,保证被校验类的方法在运行时不会做出危害假造机安全的活动,比方:

符号引用验证

符号引用验证发生在类加载过程中的剖析阶段,具体点说是 JVM 将符号引用转化为直接引用的时候(剖析阶段会介绍符号引用和直接引用)。
符号引用验证的重要目的是确保剖析阶段能正常执行,如果无法通过符号引用验证,JVM 会抛出异常,比如:

预备

预备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点必要注意:

比如我们定义了public static int value=111 ,那么 value 变量在预备阶段的初始值就是 0 而不是 111(初始化阶段才会赋值)。由于这时尚未开始执行任何Java方法,而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器()方法之中,以是把value赋值为123的动作要到类的初始化阶段才会被执行。
特别情况:如果类字段的字段属性表中存在ConstantValue属性,那在预备阶段变量值就会被初始化为ConstantValue属性所指定的初始值。
比如给 value 变量加上了 final 关键字public static final int value=111 ,那么预备阶段 value 的值就被赋值为 111。

剖析

剖析阶段是假造机将常量池内的符号引用替换为直接引用的过程。 剖析动作重要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符 7 类符号引用进行。
举个例子:在程序执行方法时,体系必要明确知道这个方法所在的位置。Java 假造机为每个类都预备了一张方法表来存放类中全部的方法。当必要调用一个类的方法的时候,只要知道这个方法在方法表中的偏移量就可以直接调用该方法了。通过剖析操作符号引用就可以直接变化为目标方法在类中方法表的位置,从而使得方法可以被调用。
综上,剖析阶段是假造机将常量池内的符号引用替换为直接引用的过程,也就是得到类或者字段、方法在内存中的指针或者偏移量。
3.初始化

初始化阶段是执行初始化方法 ()方法的过程,是类加载的末了一步,这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码)。
   注意: ()方法是编译之后自动生成的。
  对于 () 方法的调用,假造机会本身确保其在多线程环境中的安全性。由于 () 方法是带锁线程安全,以是在多线程环境下进行类初始化的话可能会引起多个线程阻塞,而且这种阻塞很难被发现。
发生的时机
概括得说,类初始化是【懒惰的】

不会导致类初始化的情况

4.类卸载

卸载类即该类的 Class 对象被 GC。
卸载类必要满意 3 个要求:
以是,在 JVM 生命周期内,由 jvm 自带的类加载器加载的类是不会被卸载的。但是由我们自定义的类加载器加载的类是可能被卸载的。
只要想通一点就好了,JDK 自带的 BootstrapClassLoader, ExtClassLoader, AppClassLoader 负责加载 JDK 提供的类,以是它们(类加载器的实例)肯定不会被接纳。而我们自定义的类加载器的实例是可以被接纳的,以是使用我们自定义加载器加载的类是可以被卸载掉的。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4