2026/2/21 17:01:32
网站建设
项目流程
文山专业网站建设联系电话,wordpress转中文,宜昌市住房城乡建设网站,网页字体尺寸设计Java 虚拟机把描述类的数据从 Class 文件加载到内存#xff0c;并对数据进行校验、转换解析和初始化#xff0c;最终形成可以被虚拟机直接使用的 Java 类型#xff0c;这个过程被称为虚拟机的类加载机制。类加载时机一个类从被加载到虚拟机内存中开始#xff0c;到卸载出内…Java 虚拟机把描述类的数据从 Class 文件加载到内存并对数据进行校验、转换解析和初始化最终形成可以被虚拟机直接使用的 Java 类型这个过程被称为虚拟机的类加载机制。类加载时机一个类从被加载到虚拟机内存中开始到卸载出内存为止它的整个生命周期将会经历加载、验证、准备、解析、初始化、使用、卸载七个阶段其中验证、准备、解析三个部分统称为连接《Java 虚拟机规范》严格规定了有且只有六种情况必须立即对类进行初始化①、遇到new、getstatic、putstatic、invokestatic这四条字节码指令能够生成这四条指令码的典型 Java 代码场景有使用new关键字实例化对象时读取或设置一个类型的静态字段时被 final 修饰已在编译期把结果放入常量池的静态字段除外调用一个类的静态方法时。②、使用java.lang.reflect包的方法对 Class 进行反射调用时如果类型没有进行过初始化、则需要触发其初始化③、当初始化类时如发现其父类还没有进行过初始化、则需要触发其父类进行初始化④、当虚拟机启动时用户需要指定一个要执行的主类包含 main() 方法的那个类虚拟机会先初始化这个主类⑤、当使用 JDK 7 新加入的动态语言支持时如果一个java.lang.invoke.MethodHandle实例最后解析的结果为REF_getStaticREF_putStaticREF_invokeStaticREF_newInvokeSpecial四种类型的方法句柄并且这个方法句柄对应的类没有进行过初始化则需要先触发其初始化⑥、当一个接口中定义了 JDK 8 新加入的默认方法被 default 关键字修饰的接口方法时如果有这个接口的实现类发生了初始化那么该接口要在其之前被初始化。类加载过程1. 加载在加载阶段虚拟机需要完成以下三件事通过一个类的全限定名来获取定义此类的二进制字节流 将这个字节流所代表的静态存储结构转换为运行时数据结构在内存中生成一个代表这个类的java.lang.Class对象作为这个类的各种数据的访问入口。《Java 虚拟机规范》并没有限制从何处获取二进制流因此可以从 JAR 包、WAR 包获取也可以从 JSP 生成的 Class 文件等处获取。2. 验证这一阶段的目的是确保 Class 文件的字节流中包含的信息符合《Java 虚拟机规范》的全部约束要求从而保证这些信息被当做代码运行后不会危害虚拟机自身的安全。验证阶段大致会完成下面四项验证文件格式验证验证字节流是否符合 Class 文件格式的规范元数据验证对字节码描述的信息进行语义分析以保证其描述的信息符合《Java 语言规范》的要求如除了java.lang.Object外所有的类都应该有父类字节码验证通过数据流分析和控制流分析确定程序语义是合法的符合逻辑的如允许把子类对象赋值给父类数据类型但不能把父类对象赋值给子类数据类型符号引用验证验证类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源。如果无法验证通过则会抛出一个java.lang.IncompatibleClassChangeError的子类异常如java.lang.NoSuchFieldError、java.lang.NoSuchMethodError等。3. 准备准备阶段是正式为类中定义的变量即静态变量被 static 修饰的变量分配内存并设置类变量初始值的阶段。4. 解析解析是 Java 虚拟机将常量池内的符号引用替换为直接引用的过程符号引用符号引用用一组符号来描述所引用的目标符号可以是任何形式的字面量只要使用时能无歧义地定位到目标即可。直接引用直接引用是指可以直接指向目标的指针、相对偏移量或者一个能间接定位到目标的句柄。整个解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符这 7 类符号引用进行解析。5. 初始化初始化阶段就是执行类构造器的()方法的过程该方法具有以下特点()方法由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生编译器收集顺序由语句在源文件中出现的顺序决定。()方法与类的构造方法即在虚拟机视角中的实例构造器()方法不同它不需要显示的调用父类的构造器Java 虚拟机会保证在子类的()方法执行前父类的()方法已经执行完毕。由于父类的()方法先执行也就意味着父类中定义的静态语句块要优先于子类变量的赋值操作。()方法对于类或者接口不是必须的如果一个类中没有静态语句块也没有对变量进行赋值操作那么编译器可以不为这个类生成()方法。接口中不能使用静态语句块但仍然有变量初始化的赋值操作因此接口与类一样都会生成()方法。Java 虚拟机必须保证一个类的()方法在多线程环境中被正确的加锁同步如果多个线程同时去初始化一个类那么只会有其中一个线程去执行这个类的()方法其他线程都需要阻塞等待。类加载器能够通过一个类的全限定名来获取描述该类的二进制字节流的工具称为类加载器。每一个类加载器都拥有一个独立的类名空间因此对于任意一个类都必须由加载它的类加载器和这个类本身来共同确立其在 Java 虚拟机中的唯一性。这意味着要想比较两个类是否相等必须在同一类加载器加载的前提下如果两个类的类加载器不同则它们一定不相等。双亲委派模型从 Java 虚拟机角度而言类加载器可以分为以下两类启动类加载器启动类加载器Bootstrap ClassLoader由 C 语言实现以 HotSpot 为例它是虚拟机自身的一部分其他所有类的类加载器由 Java 语言实现独立存在于虚拟机外部并且全部继承自java.lang.ClassLoader。从开发人员角度而言类加载器可以分为以下三类启动类加载器 (Boostrap Class Loader)负责把存放在\lib目录中或被-Xbootclasspath参数所指定的路径中存放的能被 Java 虚拟机识别的类库加载到虚拟机的内存中扩展类加载器 (Extension Class Loader)负责加载\lib\ext目录中或被java.ext.dirs系统变量所指定的路径中的所有类库。应用程序类加载器 (Application Class Loader)负责加载用户类路径ClassPath上的所有的类库。JDK 9 之前的 Java 应用都是由这三种类加载器相互配合来完成加载上图所示的各种类加载器之间的层次关系被称为类加载器的 “双亲委派模型”“双亲委派模型” 要求除了顶层的启动类加载器外其余的类加载器都应该有自己的父类加载器需要注意的是这里的加载器之间的父子关系一般不是以继承关系来实现的而是使用组合关系来复用父类加载器的代码。双亲委派模型的工作过程如下如果一个类加载器收到了类加载的请求它首先不会自己去尝试加载这个类而是把这个请求委派给父类加载器去完成每一层的类加载器都是如此因此所有的加载请求最终都应该传送到最顶层的启动类加载器只有当父加载器反馈自己无法完成这个加载请求它的搜索范围中没有找到所需的类时子加载器才会尝试自己去完成加载。基于双亲委派模型可以保证程序中的类在各种类加载器环境中都是同一个类否则就有可能出现一个程序中存在两个不同的java.lang.Object的情况。模块化下的类加载器JDK 9 之后为了适应模块化的发展类加载器做了如下变化仍维持三层类加载器和双亲委派的架构但扩展类加载器被平台类加载器所取代当平台及应用程序类加载器收到类加载请求时要首先判断该类是否能够归属到某一个系统模块中如果可以找到这样的归属关系就要优先委派给负责那个模块的加载器完成加载启动类加载器、平台类加载器、应用程序类加载器全部继承自java.internal.loader.BuiltinClassLoaderBuiltinClassLoader 中实现了新的模块化架构下类如何从模块中加载的逻辑以及模块中资源可访问性的处理。面试题简述JVM类加载过程1加载通过全类名获取类的二进制字节流。将类的静态存储结构转化为方法区的运行时数据结构。在内存中生成类的Class对象作为方法区数据的入口。2验证对文件格式元数据字节码符号引用等验证正确性。3准备在方法区内为类变量分配内存并设置为0值。4解析将符号引用转化为直接引用。5初始化执行类构造器clinit方法真正初始化。简述JVM中的类加载器BootstrapClassLoader启动类加载器加载/lib下的jar包和类。 由C编写。ExtensionClassLoader扩展类加载器 /lib/ext目录下的jar包和类。由Java编写。AppClassLoader应用类加载器加载当前classPath下的jar包和类。由Java编写。简述双亲委派机制一个类加载器收到类加载请求之后首先判断当前类是否被加载过。已经被加载的类会直接返回如果没有被加载首先将类加载请求转发给父类加载器一直转发到启动类加载器只有当父类加载器无法完成时才尝试自己加载。加载类顺序BootstrapClassLoader-ExtensionClassLoader-AppClassLoader-CustomClassLoader 检查类是否加载顺序 CustomClassLoader-AppClassLoader-ExtensionClassLoader-BootstrapClassLoader双亲委派机制的优点避免类的重复加载。相同的类被不同的类加载器加载会产生不同的类双亲委派保证了Java程序的稳定运行。保证核心API不被修改。如何破坏双亲委派机制重载loadClass()方法即自定义类加载器。如何构建自定义类加载器新建自定义类继承自java.lang.ClassLoader重写findClass、loadClass、defineClass方法以上就是这个博客的全部内容了