JVM-如何快速掌握JVM类的加载机制(JVM虚拟机类的加载机制)
在Java的世界中,JVM(Java虚拟机)是实现跨平台运行的关键技术。JVM不仅负责执行Java程序,还管理着程序运行时的内存分配、垃圾回收以及类加载等核心功能。本文将深入探讨JVM中类加载机制的奥秘。
类加载的时机
在Java程序运行期间,类加载器会在需要的时候加载类。这种加载时机通常发生在以下情况:
- 主动引用:当程序首次通过名称来引用类的任何字段、方法或者构造器时。
- 被动引用:在主动引用的情况下,如果一个类依赖于其他类,那么这些依赖的类也会被加载。比如所依赖的第三方jar包中类。
类加载的过程
类加载过程大致可以分为三个主要阶段:加载(Loading)、链接(Linking)和初始化(Initialization)。
1.加载阶段
加载阶段是类加载过程的第一步,主要完成以下工作:
- 读取二进制字节流:JVM通过类加载器从文件系统、网络或其他来源读取类的二进制字节流。
- 生成Class对象:将字节流转换成Class对象,这个对象代表了这个类在JVM中的一个类定义。
2.链接阶段
链接阶段是将加载的类与JVM中的其他类合并的过程,包括以下三个子阶段:
- 验证:确保加载的类信息符合JVM规范,没有安全问题。
- 准备:为类的静态变量分配内存,并设置默认初始值。
- 解析:将类的二进制数据中的符号引用转换为直接引用。
3.初始化阶段
初始化阶段是类加载过程的最后一步,主要任务是执行类的构造器方法 <clinit>()
,为类的静态变量赋予正确的初始值。
类加载器
JVM提供了一个分层的类加载器架构,主要包括以下几类:
1.Bootstrap ClassLoader(启动类加载器):
- 职责:负责加载Java核心类库,也就是
JAVA_HOME/jre/lib
目录下,或者被-Xbootclasspath
参数指定的路径中的类。这些类是Java运行时的基石,如java.lang.Object
等。 - 特点:这个类加载器不是用Java语言实现的,而是直接用C++语言实现的,嵌入在JVM内部。
2.Extension ClassLoader(扩展类加载器):
- 职责:负责加载
JAVA_HOME/jre/lib/ext
目录或者由系统属性java.ext.dirs
指定位置中的类库。这些类库通常是Java的扩展类库,比如javax.*
等。 - 特点:扩展类加载器是由Java语言实现的,继承自ClassLoader类。
3.Application ClassLoader(应用程序类加载器):
- 职责:也称为系统类加载器,负责加载环境变量
classpath
或系统属性java.class.path
指定路径下的类库。大多数情况下,开发人员开发的应用程序类都是由这个类加载器加载的。 - 特点:应用程序类加载器同样是由Java语言实现的,继承自ClassLoader类,开发者可以直接使用或扩展此类。
4.User ClassLoader(用户自定义类加载器):
- 职责:用户自定义类加载器不是JVM自带的,而是由开发者根据需要自定义的类加载器。它们继承自ClassLoader类,并重写
findClass
、loadClass
等方法。 - 特点:用户自定义类加载器可以控制类的加载过程,实现个性化的加载机制,比如从特定的文件系统、网络或其他资源中加载类。
类加载机制的双亲委派模型
JVM采用双亲委派模型来管理类加载器之间的交互。当一个类加载器尝试加载一个类时,它会首先委托给父类加载器去完成这个请求。如果父类加载器能够完成类加载任务,就成功返回;如果父类加载器无法完成,子类加载器才会尝试自己去加载。
这种模型的好处是:
- 避免类的多次加载:确保一个类在JVM中只被加载一次。
- 提供了一种安全机制:防止核心API库被随意篡改。
示例
1. 加载Java核心类库
场景:当应用程序需要使用Java的核心类库,如
java.lang.String
。类加载器:Bootstrap ClassLoader(启动类加载器)。
过程:
- 应用程序代码尝试使用
java.lang.String
类。 - JVM首先检查是否已经加载了这个类。
- 如果未加载,JVM会委托Bootstrap ClassLoader去加载这个类。
- Bootstrap ClassLoader负责从JRE的核心库中加载
java.lang.String
类。
2. 加载扩展库中的类
场景:应用程序需要使用Java的扩展库,如
javax.swing.JButton
。类加载器:Extension ClassLoader(扩展类加载器)。
过程:
- 应用程序代码尝试使用
javax.swing.JButton
类。 - JVM检查是否已经加载了这个类。
- 如果未加载,JVM会委托Extension ClassLoader去加载这个类。
- Extension ClassLoader负责从JRE的扩展目录(如
JAVA_HOME/jre/lib/ext
)中加载javax.swing.JButton
类。
3. 加载应用程序类路径中的类
场景:应用程序需要加载自己编写的类,如
com.example.MyClass
。类加载器:Application ClassLoader(应用程序类加载器)。
过程:
- 应用程序代码尝试使用
com.example.MyClass
类。 - JVM检查是否已经加载了这个类。
- 如果未加载,JVM会委托Application ClassLoader去加载这个类。
- Application ClassLoader负责从应用程序的类路径(由系统属性
java.class.path
指定)中加载com.example.MyClass
类。
4. 加载应用程序类路径中的jar包中的类
场景:应用程序需要加载自己编写的类依赖的jar包,如
MyJar.jar
。类加载器:Application ClassLoader(应用程序类加载器)。
过程:
- 应用程序代码尝试使用
com.example.MyClass
类。 com.example.MyClass
类依赖于第三方JAR包中的类,- JVM检查是否已经加载了依赖的这个类
- 如果未加载,JVM会委托Application ClassLoader去这个第三方JAR包中的类。
- 如果第三方JAR包中的类全部加载完毕,JVM检查是否已经加载了这个类
com.example.MyClass
- 如果未加载,JVM会委托Application ClassLoader去加载这个类。
- Application ClassLoader负责从应用程序的类路径(由系统属性
java.class.path
指定)中加载com.example.MyClass
类。
总结
JVM的类加载机制是Java程序能够顺利运行的基础。理解这一机制不仅有助于我们更好地编写和维护Java程序,还能帮助我们解决运行时遇到的各种类加载问题。
参考:《深入理解Java虚拟机》