Java中类加载器举例详解

 更新时间:2026年01月07日 11:18:10   作者:小白qzx  
Java类加载器是Java语言的核心组件之一,负责从文件系统或网络中加载Class文件,Class文件在文件开头有特定的文件标识,这篇文章主要介绍了Java中类加载器的相关资料,需要的朋友可以参考下

类加载器简介

Java程序被编译器编译之后成为字节码文件(.class文件),当程序需要某个类时,虚拟机便会将对应的class文件进行加载,创建出对应的Class对象。而这个将class文件加载到虚拟机内存的过程,便是类加载。

类加载器负责在运行时将Java类动态加载到JVM(Java虚拟机),是JRE(Java运行时环境)的一部分。由于类加载器的存在,JVM无需了解底层文件或文件系统即可运行Java程序。并且Java类不会一次全部加载到内存中,而是在需要时才会加载到内存中。

类加载的过程

类的生命周期通常包括:加载、链接、初始化、使用和卸载。上图中包含了类加载的三个阶段:加载阶段、链接阶段和初始化阶段。如果将这三个阶段再拆分细化包括:加载、验证、准备、解析和初始化。

这几个阶段的作用简单概况一下:

  • 加载:通过一个类的完全限定查找类字节码文件,转化为方法区运行时的数据结构,创建一个代表该类的Class对象。
  • 验证:确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。
  • 准备:为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值。不包含被final修饰的static变量,因为它在编译时已经分配了。
  • 解析:将常量池内的符号引用转换为直接引用的过程,比如(main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用。如果符号引用指向一个未被加载的类,或者未被加载类的字段或方法,那么解析将触发这个类的加载。
  • 初始化:类加载最后阶段,执行静态初始化器和静态初始化成员变量。对类的静态变量初始化为指定的值,执行静态代码块。

在上述类加载的过程中,虚拟机提供了三种类加载器:启动(Bootstrap)类加载器、扩展(Extension)类加载器、系统(System)类加载器(也称应用类加载器),此外还可以自定义类加载器。

启动(Bootstrap)类加载器:

系统中最顶层、最核心的加载器,是 JVM 与生俱来的一部分,负责加载 JDK 核心类库,奠定 Java 程序运行的基础,由 JVM 的底层代码(C/C++,如 HotSpot 虚拟机)实现,无法在 Java 代码中直接获取其对象(调用 getClassLoader() 返回 null)。

加载范围:加载 JDK 核心类库

  • JDK 8 及之前:加载 <JAVA_HOME>/jre/lib 目录下的核心 JAR 包(如 rt.jarcharsets.jarsunrsasign.jar 等),这些 JAR 是 JVM 识别的「核心类库」(非该目录下的任意 JAR 不会被加载);
  • JDK 9+(模块化):移除了 rt.jar 等打包方式,核心类以「模块」形式存储在 <JAVA_HOME>/lib/modules(模块化镜像文件),启动类加载器负责加载 java.base 等核心模块(如 java.langjava.io 所属的模块)。

扩展(Extension)类加载器:

扩展类加载器是启动类加载器的子类,Java语言编写,由sun.misc.Launcher$ExtClassLoader实现,父类加载器为启动类加载器,负责加载标准核心Java类的扩展。

扩展类加载器从JDK扩展目录(通常是$JAVA_HOME/lib/ext目录)或java.ext.dirs系统属性中指定的任何其他目录进行自动加载。

系统/应用(System)类加载器:

系统类加载器负责将所有应用程序级类加载到JVM中。它加载在类路径环境变量,-classpath或-cp命令行选项中找到的文件。它是扩展类加载器的子类。

自定义类加载器:

在大多数情况下,内置的三种类加载器就足够了。但是,在需要从本地硬盘驱动器或网络中加载类的情况下,需要打破双亲委派机制,则可能需要使用自定义类加载器。

下面介绍类加载器的加载机制--双亲委派机制。

双亲委派机制:

从图中可以清晰的看到双亲委派机制的具体过程:加载某个类时会先层层委托给父加载器寻找目标类加载,如果父加载器在自己的加载类路径下都找不到目标类,尝试向下加载。比如我们自己随便写的User类,最先会找应用程序类加载器加载,应用程序类加载器会先委托扩展类加载器加载,扩展类加载器再委托启动类加载器,顶层启动类加载器在自己的类加载路径里找了半天没找到User类,则向下退回加载User类的请求,扩展类加载器收到回复就自己加载,在自己的类加载路径里找了半天也没找到User类,又向下退回User类的加载请求给应用程序类加载器,应用程序类加载器于是在自己的类加载路径里找User类,结果找到了就自己加载了。双亲委派机制说简单点就是,先找父亲加载,不行再由儿子自己加载。

我们来看一下loadclass的源码:

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

此方法负责加载给定名称参数的类。name参数为类的全限定名。先查找是否已加载该类,若无,则判断有无父类加载器parent,尝试给父类加载器去loadclass,其实就是一个递归的顺序。

为什么要双亲委派机制:

沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止
核心API库被随意篡改( 可以自己写一个String 类 会发现报错 )

避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加
载一次,保证被加载类的唯一性

打破双亲委派机制示例:

1、tomcat:tomcat就是属于一个web 程序,可能部署俩个应用程序,不同的应用程序可能会依赖一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离,就需要打破双亲委派机制。

2、热部署:热部署要求 “修改类 / JSP 后,无需重启应用即可生效”,而双亲委派下,类加载器加载类后,该类会被 JVM 缓存,类加载器不销毁,类就无法重新加载。因此需要自定义类加载器 + 销毁重建。

总结

到此这篇关于Java中类加载器的文章就介绍到这了,更多相关Java类加载器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot随机端口启动的实现

    SpringBoot随机端口启动的实现

    本文主要介绍了SpringBoot随机端口启动的实现,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • SpringBoot使用H2嵌入式数据库的实例代码

    SpringBoot使用H2嵌入式数据库的实例代码

    本文通过实例代码给大家介绍了SpringBoot使用H2嵌入式数据库的相关知识,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-10-10
  • Java遗传算法之冲出迷宫

    Java遗传算法之冲出迷宫

    这篇文章首先详细介绍了什么是遗传算法,然后通过遗传算法的思想用实例解析使用遗传算法解决迷宫问题,需要的朋友可以参考下
    2017-09-09
  • SpringBoot打印系统执行的sql语句及日志配置指南

    SpringBoot打印系统执行的sql语句及日志配置指南

    这篇文章主要给大家介绍了关于SpringBoot打印系统执行的sql语句及日志配置的相关资料,在Java SpringBoot项目中如果使用了Mybatis框架,默认情况下执行的所有SQL操作都不会打印日志,需要的朋友可以参考下
    2023-10-10
  • SpringBoot AOP @Pointcut切入点表达式排除某些类方式

    SpringBoot AOP @Pointcut切入点表达式排除某些类方式

    这篇文章主要介绍了SpringBoot AOP @Pointcut切入点表达式排除某些类方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Java线程的新建和就绪状态实例分析

    Java线程的新建和就绪状态实例分析

    这篇文章主要介绍了Java线程的新建和就绪状态,结合实例形式分析了java线程的创建、调用等相关操作技巧,需要的朋友可以参考下
    2019-09-09
  • Java中的Pair详细

    Java中的Pair详细

    这篇文章主要介绍Java中的很有意思的Pair,下面文章会以Pair用法展开,感兴趣的小伙伴可以参考下面文章的具体内容
    2021-10-10
  • 在SpringBoot中实现断点续传的实例代码

    在SpringBoot中实现断点续传的实例代码

    在 Spring Boot 或任何其他 web 开发框架中,断点续传是一种技术,允许文件的传输在中断后可以从中断点重新开始,而不是从头开始,种技术在处理大文件或在不稳定的网络环境中尤为重要,本文给大家介绍了SpringBoot中实现断点续传的实例代码,需要的朋友可以参考下
    2024-07-07
  • java中的编码转换过程(以utf8和gbk为例)

    java中的编码转换过程(以utf8和gbk为例)

    这篇文章主要介绍了java中的编码转换过程(以utf8和gbk为例),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • Java简单实现对一串数字采用相应的加密策略后传输

    Java简单实现对一串数字采用相应的加密策略后传输

    下面小编就为大家带来一篇Java简单实现对一串数字采用相应的加密策略后传输。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-09-09

最新评论