一篇文章弄懂JVM类加载机制过程以及原理

那么让我们来详细讲解一下“一篇文章弄懂JVM类加载机制过程以及原理”的完整攻略。

1. JVM类加载机制基础

在深入了解JVM类加载机制的过程之前,我们需要先了解一些相关的基础知识。

1.1 类、类加载器和类加载

Java中,我们通常所说的类是指Java类,而Java类的定义是以.java文件为载体,通过编译器(如javac)将其转换为.class文件后生成,.class文件就是Java类的二进制字节码形式。Java虚拟机通过字节码来执行Java程序,因此Java虚拟机加载的是.class文件。

类加载器是Java虚拟机的组成部分之一,用于把类加载到内存中并生成Java类对象。一个Java类在被加载到内存中之前,需要经过三个步骤:加载、链接和初始化。其中加载即是将Java类的字节码文件读取到内存中,并创建一个对应的java.lang.Class对象的过程。

1.2 类的生命周期

一个Java类从被加载到虚拟机内存中开始,到虚拟机将其卸载出内存为止,其整个生命周期可以划分为如下7个阶段:

  1. 加载:将类的.class文件读入内存,并在堆空间创建一个用于描述类的java.lang.Class对象。
  2. 验证:验证类的字节码文件的正确性,包括文件格式、语义、二进制兼容性等。
  3. 准备:为类的静态变量分配内存,并设置默认值。
  4. 解析:将常量池中的符号引用转换为直接引用,即将常量池中的类名、方法名、字段名等转换为内存地址。
  5. 初始化:为类的静态变量赋值,并执行静态代码块。
  6. 使用:类从虚拟机中加载后,才能被Java程序调用使用。
  7. 卸载:类在虚拟机内不再使用,卸载类并释放内存。

1.3 类加载器分类

Java虚拟机的类加载器有三种,按照类加载器的启动顺序可分为以下三类:

  1. 启动类加载器(Bootstrap ClassLoader):用于加载Java虚拟机核心库中的类,如rt.jarresources.jar等。
  2. 扩展类加载器(Extension ClassLoader):用来加载Java虚拟机扩展目录中的类库,即$JAVA_HOME/jre/lib/ext目录下的所有jar包。
  3. 应用程序类加载器(Application ClassLoader):又称为系统类加载器,用于加载当前应用程序类路径(即CLASSPATH)下的全部类库。

1.4 双亲委派模型

Java虚拟机的类加载器采用了一种父亲委派的加载机制,即双亲委派模型。当一个类被加载到虚拟机中时,会先从启动类加载器开始依次向下搜索,直到找到该类为止。如果找不到,则会抛出ClassNotFoundException异常。

具体来说,类加载器在加载一个类时,首先会尝试使用其父亲类加载器去加载该类,只有在父亲类加载器无法加载该类时才会尝试自己加载该类。这样做的好处是防止重复加载,提高安全性和系统稳定性。

在实际应用中,一般自定义类加载器都是基于父类加载器进行构建的,而双亲委派模型也是Java虚拟机对于Java类的安全保障机制之一。

2. JVM类加载机制过程

了解了上述的基础知识之后,我们可以开始深入了解JVM类加载机制的过程。

2.1 类加载的过程

类加载的过程是在“使用”阶段之前,即“初始化”阶段。在“初始化”阶段,Java虚拟机才会真正执行类的初始化,也才算是对该类的主动使用。

类加载的过程主要分为以下三个阶段:

  1. 加载:从文件系统或网络中读入字节码流,将其转换成java.lang.Class对象。
  2. 链接:将Java类的.class文件中的符号引用(如类和接口的全限定名、字段和方法的名称等)解析为直接引用(如在运行时内存中的对象、方法区中的指针等),并把刚刚加载的.class文件中的变量初始化为默认值(如0或null)。
  3. 初始化:执行类的初始化代码,包括静态初始化代码块和变量初始化等。当一个类被初始化时,依次执行此类父类的静态代码块和构造方法。

2.2 类加载器的实现过程

Java虚拟机在加载Java类时,会使用Java类加载机制来完成下面的三个过程:

  1. 将Java类的字节码文件加载到Java虚拟机内存中:每个Java类都有一个与之对应的java.lang.Class对象,该对象记录了这个类的完整信息。
  2. 验证Java类的字节码文件的正确性:验证阶段需要检查字节码文件的文件格式(Magic Number)、语义合法性和二进制兼容性等。
  3. 让内存中的Java类“可用”:包括初始化类的数据和代码,初始化阶段主要是执行类的静态初始化代码块和静态变量初始化。为了实现这些操作,Java虚拟机需要使用到类加载器。

类加载器在加载Java类的过程中,通常会遵循如下流程:

  1. 检查类是否已经被加载过,如果已经被加载过,则直接返回。
  2. 如果类没有被加载过,则让其父加载器(除了启动类加载器)试图加载该类。
  3. 如果父加载器无法加载该类,则调用自己的加载方法进行加载。

一个类的加载过程可以简单地概括为:类加载器在加载某个类时,首先会代理向其父类加载器发送加载请求,以此类推,直到请求传递到最顶层,即启动类加载器。如果最顶层的类加载器可以找到该类,则将该类加载到内存中;如果所有的类加载器都无法找到该类,则会抛出ClassNotFoundException异常。

2.3 类的双亲委派机制

Java虚拟机的类加载器采用了双亲委派机制。当Java虚拟机在加载某个类时,会依次向其父类加载器发送加载请求,直到顶级类加载器——启动类加载器。如果启动类加载器也无法找到该类,则会抛出ClassNotFoundException异常。

采用双亲委派机制的好处在于:

  • 可以防止类的重复加载:当某个类在某个类加载器的加载范围内已经被加载,其他类加载器如果需要加载该类,只需要将该请求委托给其父类加载器进行处理即可。
  • 可以保证Java核心类的安全:Java核心类库都是由启动类加载器加载,而启动类加载器是由Java虚拟机实现的,因此任何不受信任的代码都无法通过其父加载器加载核心类库,从而保证了Java核心类的安全性。

3. 示例说明

下面通过两个例子来帮助更好地理解JVM类加载机制的过程。

示例1

假设有如下Java代码:

public class Test {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

当我们编译运行该代码时,Java虚拟机需要先加载该类,然后才能开始执行。

在执行Java代码时,Java虚拟机首先需要将Java类的.class文件读入内存,并在堆空间创建一个java.lang.Class对象。在进行类加载的过程中,Java虚拟机采用了双亲委派机制。具体来说,Java虚拟机会按照以下的顺序进行类加载:

  1. 首先,Java虚拟机会由应用程序类加载器(也叫系统类加载器)尝试加载该类,但该类不在应用程序类加载器的加载范围内。
  2. 随后,应用程序类加载器会代理向扩展类加载器发送加载请求,扩展类加载器无法加载该类,因此继续向其父加载器——启动类加载器发送加载请求。
  3. 启动类加载器成功加载该类。

最终,Java虚拟机将该类加载到内存中,并创建一个对应的java.lang.Class对象,该对象用于描述该类的完整信息。

示例2

假设我们需要自定义一个类加载器,并使用其去加载一个类。我们可以在自定义类加载器中重写findClass方法,并将该类放在指定的Class路径下:

public class MyClassLoader extends ClassLoader {

    /**
     * 定义加载的目录
     */
    private String classPath;

    public MyClassLoader(String classPath) {
        this.classPath = classPath;
    }

    /**
     * 重写findClass方法
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 将类名转换为对应的文件路径
        String fileName = name.replace(".", "/");
        // 拼接完整的文件路径
        String filePath = classPath + "/" + fileName + ".class";
        // 读取字节码文件到内存中
        byte[] classBytes = loadClassBytes(filePath);
        if (classBytes == null) {
            throw new ClassNotFoundException("Cannot load class " + name);
        }
        // 将字节码转换为Class对象
        return defineClass(name, classBytes, 0, classBytes.length);
    }

    /**
     * 读取字节码文件到内存中
     */
    private byte[] loadClassBytes(String fileName) {
        // 从文件中读取字节码文件
        // ...
    }
}

我们可以使用以下代码来测试该类加载器:

public static void main(String[] args) throws Exception {
    // 创建自定义类加载器
    MyClassLoader myClassLoader = new MyClassLoader("/path/to/class");
    // 在自定义类加载器中载入某个类
    Class<?> clazz = myClassLoader.loadClass("com.example.MyClass");
    // 创建该类的实例
    Object instance = clazz.newInstance();
    // 调用该类的某个方法
    Method method = clazz.getMethod("xxx", String.class);
    method.invoke(instance, "Hello, World!");
}

在这个例子中,我们自定义了一个类加载器,并将其用于载入一个指定目录下的类。类加载器会先尝试使用父类加载器加载该类,如果父类加载器无法加载,则代理调用自己的类加载方法进行加载。

值得注意的是,在本例中,我们自定义了一个类加载器,并将字节码文件读入到内存中。在实际应用中,JVM类加载机制也是如此,Java虚拟机将Java类的字节码文件读取到内存中,并创建一个对应的java.lang.Class对象。类加载器的作用在于将Java类加载到内存中,并生成对应的Class对象。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:一篇文章弄懂JVM类加载机制过程以及原理 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • SpringSecurity OAtu2+JWT实现微服务版本的单点登录的示例

    实现微服务版本的单点登录需要结合SpringSecurity、OAuth2和JWT三个技术点。 首先,关于OAuth2的基础概念和流程可以参考我的博客文章:OAuth2授权模式详解。 接下来就是示例说明: 示例1:SpringBoot微服务注册 在OAuth2客户端程序中添加以下依赖: <dependency> <groupId>or…

    Java 2023年6月3日
    00
  • SSM框架整合之Spring+SpringMVC+MyBatis实践步骤

    SSM框架整合之Spring+SpringMVC+MyBatis实践步骤 SSM框架整合是Java Web开发中常用的一种方式,它将Spring、SpringMVC和MyBatis三个框架整合在一起,可以提高开发效率和代码质量。本文将详细介绍SSM框架整合的实践步骤,并提供两个示例说明。 实践步骤 步骤一:创建Maven项目 首先,我们需要创建一个Maven…

    Java 2023年5月17日
    00
  • 深度分析java dump文件

    以下是“深度分析java dump文件”的完整攻略: 什么是Java Dump文件 Java Dump文件是在Java应用程序运行时出现异常或死锁等问题时自动或手动导出的一种快照文件。它记录了Java虚拟机(JVM)在某个时间点上的内存状态,可以用于问题排查和调试。 如何生成Java Dump文件 可以通过以下两种方式生成Java Dump文件: JCons…

    Java 2023年5月20日
    00
  • Java操作FreeMarker模板引擎的基本用法示例小结

    要在Java中使用FreeMarker模板引擎进行模板渲染,需要经历以下几个步骤: 引入FreeMarker依赖 在Maven项目中,可以在pom.xml文件中添加以下依赖项: <dependency> <groupId>org.freemarker</groupId> <artifactId>freemark…

    Java 2023年6月15日
    00
  • Hibernate validator使用以及自定义校验器注解

    Hibernate Validator是一个基于JSR 380规范的Java Bean验证库,它能够为Java Bean的属性提供各种验证规则,比如非空、长度、邮箱格式等。在本文中,我们将学习如何使用Hibernate Validator进行Java Bean的验证,同时介绍如何自定义校验器注解。 1. 添加Hibernate Validator依赖 首先,…

    Java 2023年5月20日
    00
  • 将properties文件的配置设置为整个Web应用的全局变量实现方法

    将properties文件的配置设置为整个Web应用的全局变量实现方法可以采用ServletContextListener监听器实现。 实现步骤如下: 创建一个Properties文件,例如config.properties,用于存储Web应用的配置信息。该文件的格式类似于以下示例: database.driver=com.mysql.jdbc.Driver…

    Java 2023年6月15日
    00
  • java后台批量下载文件并压缩成zip下载的方法

    请允许我给出完整的“java后台批量下载文件并压缩成zip下载的方法”的攻略: 1. 需求分析 首先,我们需要明确需求,由于是后台批量下载文件并压缩成zip下载,所以我们需要考虑以下几个方面: 获取文件路径列表 批量下载文件 压缩成zip文件 提供zip文件下载 2. 操作步骤 2.1 获取文件路径列表 我们可以通过一个方法获取文件路径列表,该方法需要传入文…

    Java 2023年5月19日
    00
  • IDEA实现JDBC的操作步骤

    下面是详细讲解“IDEA实现JDBC的操作步骤”的完整攻略: 1. 环境搭建 首先需要搭建Java项目的环境,使用IntelliJ IDEA的话可以通过以下步骤: 打开IntelliJ IDEA,点击 “Create New Project” 创建一个新的Java项目。 在 “New Project” 窗口中,选择 “Java” 项目类型,并选择需要使用的J…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部