JVM-如何快速掌握JVM类的加载机制(JVM虚拟机类的加载机制)

 

在Java的世界中,JVM(Java虚拟机)是实现跨平台运行的关键技术。JVM不仅负责执行Java程序,还管理着程序运行时的内存分配、垃圾回收以及类加载等核心功能。本文将深入探讨JVM中类加载机制的奥秘。




类加载的时机

在Java程序运行期间,类加载器会在需要的时候加载类。这种加载时机通常发生在以下情况:

  1. 主动引用:当程序首次通过名称来引用类的任何字段、方法或者构造器时。
  2. 被动引用:在主动引用的情况下,如果一个类依赖于其他类,那么这些依赖的类也会被加载。比如所依赖的第三方jar包中类。

类加载的过程

类加载过程大致可以分为三个主要阶段:加载(Loading)、链接(Linking)和初始化(Initialization)。



1.加载阶段

加载阶段是类加载过程的第一步,主要完成以下工作:

  • 读取二进制字节流:JVM通过类加载器从文件系统、网络或其他来源读取类的二进制字节流。
  • 生成Class对象:将字节流转换成Class对象,这个对象代表了这个类在JVM中的一个类定义。

2.链接阶段

链接阶段是将加载的类与JVM中的其他类合并的过程,包括以下三个子阶段:

  1. 验证:确保加载的类信息符合JVM规范,没有安全问题。
  2. 准备:为类的静态变量分配内存,并设置默认初始值。
  3. 解析:将类的二进制数据中的符号引用转换为直接引用。

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虚拟机》