一篇文章弄懂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对象。

阅读剩余 75%

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

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

相关文章

  • 在Win10上安装Tomcat服务器及配置环境变量的详细教程(图文)

    在Win10上安装Tomcat服务器及配置环境变量的详细教程: 准备工作 官方网站下载Tomcat服务器压缩文件,例如tomcat-8.5.57.tar.gz 安装JDK,推荐版本为JDK8或JDK11,JDK11及以上版本,Tomcat需版本在9及以上 确认JDK环境变量已配置 安装Tomcat 解压Tomcat服务器压缩文件到指定目录。例如,将压缩文件解…

    Java 2023年5月19日
    00
  • 什么是代码优化?

    以下是关于代码优化的完整使用攻略: 什么是代码优化? 代码优化是指通过改进代码的设计、算法、数据结构、代码实现等方面,提高代码的运行效率和响应速度,减少资源占用和延迟等问题。代码优化的目的是提高程序的性能,使其更加高效、稳定和可靠。 代码优化的原则 代码优化需要遵循以下原则: 不要过早优化:在代码编写的早期,不要早地考虑优化问题,应该先保证代码的正确性和可读…

    Java 2023年5月12日
    00
  • 在Windows10中安装TomCat的教程图解

    以下是“在Windows10中安装TomCat的教程图解”的完整攻略: 准备工作 在安装Tomcat之前,需要先确保以下几点: 确认Windows10已经安装了Java开发环境(JDK)。可以到Oracle官网下载JDK,也可以通过搜索引擎搜索其他可信的JDK下载地址。 下载Tomcat安装包。可以到Tomcat官网下载最新的Tomcat安装包,也可以通过搜…

    Java 2023年5月19日
    00
  • java新手入门——String类详解

    Java 新手入门 —— String类详解攻略 简介 String 类是 Java 中比较重要的一个类,所有的字符串都是用它来表示的。本攻略将会详细讲解 String 类的各种方法的用法,并通过代码示例来帮助理解。 创建字符串 可以使用两种方式来创建字符串: 使用双引号(” “) 把字符串定义在一个变量中; 使用 String 类的构造函数来创建字符串。 …

    Java 2023年5月19日
    00
  • Sprint Boot @TransactionalEventListener使用方法详解

    在Spring Boot中,@TransactionalEventListener注解用于在事务提交后异步地处理事件。使用@TransactionalEventListener注解可以确保事件处理程序在事务提交后才会执行,从而避免了在事务未提交时处理事件可能导致的问题。本文将详细介绍@TransactionalEventListener注解的作用和使用方法,…

    Java 2023年5月5日
    00
  • 详解redis与spring的整合(使用缓存)

    下面是关于“详解redis与spring的整合(使用缓存)”的完整攻略。 一、准备工作 安装Redis,并启动Redis服务。 在pom.xml文件中添加Redis、Jedis、Spring Data Redis的依赖。 二、使用Spring Data Redis连接Redis 在Spring配置文件中,我们可以使用以下配置来连接Redis。 <bea…

    Java 2023年5月20日
    00
  • Java日期时间操作的方法

    下面就给您介绍Java日期时间操作的方法的完整攻略。 1. 日期时间类 Java中有多种日期时间类,包括Date、Calendar、DateFormat、SimpleDateFormat、Instant等。其中,Date和Calendar是Java SE 1.1版本引入的,而Instant是Java SE 8版本新增的类。 1.1 Date类 java.ut…

    Java 2023年5月20日
    00
  • 一文带你深入了解Java中延时任务的实现

    一文带你深入了解Java中延时任务的实现 延时任务(Delayed task)是一种可以在一定时间后触发的任务。在Java中,我们可以通过多种方式来实现延时任务,包括使用Timer/TimerTask类、ScheduledExecutorService类、和DelayQueue类等。 使用Timer/TimerTask类实现延时任务 Timer/TimerT…

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