JVM--类加载机制
类加载机制是Java虚拟机(JVM)运行时动态加载类的过程,确保了程序在执行过程中所需的类能够被正确加载到内存中。这一过程不仅包括将编译后的.class
文件转换为内存中的类实例,还涉及到对这些类数据的校验、准备、解析和初始化等步骤。下面我们将深入探讨类加载机制的具体内容。
类加载的生命周期
根据Java虚拟机规范,类从被加载到虚拟机开始直到卸载出内存为止,其整个生命周期可以分为七个阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)以及卸载(Unloading)。其中,验证、准备和解析这三个阶段统称为连接(Linking),而加载、连接及初始化构成了类加载的主要部分。
加载(Loading)
加载是类加载过程的第一步,主要完成以下三件事情:
- 通过全类名获取定义此类的二进制字节流;
- 将字节流所代表的静态存储结构转换为方法区的运行时数据结构;
- 在内存中生成一个代表该类的
java.lang.Class
对象,作为方法区这些数据的访问入口。
在这个过程中,JVM并不限制二进制字节流的具体来源,它可以来自本地文件系统、网络传输、数据库记录甚至是动态生成的数据。加载这一步主要是通过我们后面要讲到的类加载器来完成的。
验证(Verification)
验证阶段的目的是为了确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。验证阶段主要包括四个子阶段:文件格式验证、元数据验证、字节码验证以及符号引用验证。虽然这个阶段消耗资源较多,但对于维护程序的安全性和稳定性至关重要。如果程序中所有的代码都已经经过充分测试并且信任度很高,则可以在生产环境中使用-Xverify:none
参数来关闭大部分的类验证措施以缩短类加载时间。
准备(Preparation)
准备阶段正式为类变量分配内存并设置类变量初始值,这些内存都将在方法区中分配。需要注意的是,此时进行内存分配的仅包括类变量(即静态变量),而不包括实例变量。对于基本类型的类变量,在此阶段会设置为默认的零值;而对于引用类型的类变量,则会被赋予null
值。特殊情况是带有final
修饰符的基本类型类变量,它们会在准备阶段就被赋予指定的常量值。
解析(Resolution)
解析阶段将常量池内的符号引用替换为直接引用。符号引用是以一组符号来描述目标,并且在编译时确定;而直接引用则是指可以直接定位到目标的地址或偏移量。解析阶段的一个重要任务就是检查符号引用是否能够成功转化为直接引用,例如确认类之间是否存在继承关系、字段和方法是否存在等。
初始化(Initialization)
初始化阶段是类加载过程中的最后一个阶段,也是唯一可能由程序员控制的部分。在此阶段,类中定义的所有静态变量将会按照声明顺序执行相应的赋值操作,并且会调用静态初始化块中的代码。此外,如果存在父类,则必须先完成父类的初始化之后才能继续子类的初始化工作。
类加载器与双亲委派模型
类加载器是实现类加载过程的核心组件之一,它负责从不同的源位置读取类的二进制字节流,并将其转换成内存中的类表示形式。JVM内置了三种主要的类加载器:启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)以及应用程序类加载器(Application ClassLoader),它们之间形成了所谓的“双亲委派模型”。每当尝试加载一个新的类时,当前类加载器首先会委托给它的父加载器去处理,只有当父加载器无法找到对应的类时才会自己尝试加载。这种设计有效地避免了类的重复加载,并增强了系统的安全性。
除了上述标准的类加载器之外,用户还可以创建自定义类加载器来满足特定的需求,比如加载加密后的类文件或者实现热部署功能。自定义类加载器通常需要重写ClassLoader
抽象类中的findClass()
方法或loadClass()
方法。
原文地址:https://blog.csdn.net/Maxianghua95/article/details/144424924
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!