目录
一、类加载步骤
1.加载(loading)
- 在这个阶段,JVM会通过类的全限定名来获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成这个类的
java.lang.Class
对象,作为方法区这个类的各种数据的访问入口。 - 这个过程通常由类加载器来完成,它从文件系统、网络或其他源读取
.class
文件,并转化为一个java.lang.Class
对象。 - 对于数组类型,没有对应的字节码文件,它是由JVM在运行期动态生成的。
2.验证(Verification)
- 确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
- 验证阶段可以分为文件格式验证、元数据验证、字节码验证和符号引用验证。
- 文件格式验证
- 验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理
- 主要目的:保证输入的字节流能正确的解析并存储于方法区内
- 这阶段的验证基于二进制字节流
- 元数据验证
- 对字节码描述的信息进行语义分析,保证其描述的信息符合Java语言规范
- 主要目的:对类的元数据信息进行语义校检,保证不存在不符合Java语言规范的元数据信息(数据类型)
- 这阶段的验证以及后续验证都基于方法区
- 字节码验证
- 通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
- 对类的方法体进行校检分析,保证被校检的方法在运行时不会危害虚拟机的安全
- 符号引用验证
- 发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在连接的第三个阶段–解析中发生
- 符号引用验证可以看做是对类自身以外的信息(常量池中的各种符号引用)进行匹配性校检
- 文件格式验证
- 验证阶段非常重要,但不一定必要,如果所运行的全部代码都已经被反复使用和验证过,那么在实施阶段可以使用
-Xverify:none
参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。
3.准备(Preparation)
- JVM为类变量分配内存并设置类变量的默认初始值。
- 这些变量使用的内存都在方法区中进行分配。
4.解析(Resolution)
- JVM将常量池内的符号引用替换为直接引用的过程。
- 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符七类符号引用。分别对应于常量池的
CONSTANT_Class_info
、CONSTANT_Dieldref_info
、CONSTANT_Methodref_info
、CONSTANT_InterfaceMethodref_info
、CONSTANT_MethodType_info
、CONSTANT_MethodHandle_info
、CONSTANT_InvokeDynamic_Info
。 - 解析阶段将这些引用转化为实际的内存地址。
5.初始化(Initialization)
- JVM负责对类进行初始化,主要包括执行类构造器
<clinit>()
方法的过程。 <clinit>()
方法是由编译器自动为每个类收集类变量的赋值动作和静态代码块中的语句合并而成。- 在准备阶段完成之后,JVM会执行
<clinit>()
方法。
二、类加载器
1.启动类加载器(Bootstrap Class Loader)
- 它是虚拟机自带的类加载器,负责加载Java的核心库(如
rt.jar
,resources.jar
,charsets.jar
等)。 - 启动类加载器是不由Java语言编写的,通常被认为是虚拟机的一部分。
- 它主要加载位于
<JAVA_HOME>/lib
目录或者被-Xbootclasspath
参数指定的路径中的类。
2.扩展类加载器(Extension Class Loader)
- 也称为标准扩展类加载器。
- 它负责加载JRE的扩展目录
<JAVA_HOME>/lib/ext
或者由Java系统变量java.ext.dirs
指定位置中的JAR包。 - 它是
java.security.SecureClassLoader
的子类。
3.应用程序类加载器(Application Class Loader)
- 这是与
ClassLoader.getSystemClassLoader()
方法返回的类加载器相对应。 - 它负责加载环境变量
CLASSPATH
或者系统属性java.class.path
指定路径下的类库。 - 这个类加载器是用户自定义加载器的默认父加载器。
4.自定义加载器(User-Defined Class Loader)
- Java允许开发者通过继承
java.lang.ClassLoader
类的方式自定义类加载器。 - 开发者可以重写
findClass()
方法来实现自己的加载逻辑。
5.系统类加载器(System Class Loader)
- 这个加载器是
Application Class Loader
的同义词,为了区分概念上的不同,在某些上下文中单独称呼。
三、三个重要特性
1.双亲委派机制
- 类加载器之间的一种层次关系。当一个类加载器收到加载请求时,它首先会将这个请求委派给父类加载器,一直向上委派到顶层的启动类加载器,只有当父类加载器无法完成这个加载请求时,子类加载器才会尝试自己去加载。
- 双亲委派机制的过程:
- 当
AppClassLoader
加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader
去完成。 - 当
ExtClassLoader
加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader
去完成。 - 如果
BootStrapClassLoader
加载失败(例如在$JAVA_HOME/jre/lib
里未查找到该class
),会使用ExtClassLoader
来尝试加载; - 若
ExtClassLoader
也加载失败,则会使用AppClassLoader
来加载,如果AppClassLoader
也加载失败,则会报出异常ClassNotFoundException
。
- 当
2.缓存机制
- 缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效
3.独立性
- 不同的类加载器可以加载相同名称(全限定名)的类,这些类彼此不会产生冲突,因为属于不同的命名空间。