一篇文章读懂Java类加载器

OpenClaw实战系列06:持久记忆与RAG知识库构建——让AI真正“记住”你 本文介绍了OpenClaw AI助理的持久记忆与RAG知识库构建功能。OpenClaw采用"文件即真理"的独特设计,所有记忆都以Markdown文件形式存储,支持直接编辑和Git版本管理。记忆系统分为三层:长期精炼记忆(MEMORY.md)、日常日志(按日期存储)和归档记忆。文章详细说明了环境配置步骤,包括安装必要组件、创建目录结构、设置API密钥,并提供了OpenClaw主配置文件的完整参数说明。该系统实现了跨会话、跨平台的持久化记忆,使AI助理能真正理解用户需求。 阅读详情

Java类加载器算是一个老生常谈的问题,大多Java工程师也都对其中的知识点倒背如流,最近在看源码的时候发现有一些细节的地方理解还是比较模糊,正好写一篇文章梳理一下。

关于Java类加载器的知识,网上一搜一大片,我自己也看过很多文档,博客。资料虽然很多,但还是希望通过本文尽量写出一些自己的理解,自己的东西。如果只是重复别人写的内容那就失去写作的意义了。

类加载器结构
类加载器结构

名称解释:

  1. 根类加载器,也叫引导类加载器、启动类加载器。由于它不属于Java类库,这里就不说它对应的类名了,很多人喜欢称BootstrapClassLoader。本文都称之为根类加载器。
    加载路径:<JAVA_HOME>\lib
  2. 扩展类加载器,对应Java类名为ExtClassLoader,该类是sun.misc.Launcher的一个内部类。
    加载路径:<JAVA_HOME>\lib\ext
  3. 应用类加载器,对应Java类名为AppClassLoader,该类是sun.misc.Launcher的一个内部类。
    加载路径:用户目录
//可以通过这种方式打印加载路径
System.out.println("boot:"+System.getProperty("sun.boot.class.path"));
System.out.println("ext:"+System.getProperty("java.ext.dirs"));
System.out.println("app:"+System.getProperty("java.class.path"));

重点说明:

  1. 根类加载器对于普通Java工程师来讲可以理解成一个概念上的东西,因为我们无法通过Java代码获取到根类加载器,它属于JVM层面。
  2. 除了根类加载器之外,其他两个扩展类加载器和应用类加载器都是通过类sun.misc.Launcher进行初始化,而Launcher类则由根类加载器进行加载。

看下Launcher初始化源码:

public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            //初始化扩展类加载器,注意这里构造函数没有入参,即无法获取根类加载器
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            //初始化应用类加载器,注意这里的入参就是扩展类加载器
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }

        //设置上下文类加载器,这个后面会详细说
        Thread.currentThread().setContextClassLoader(this.loader);

       //删除了一些安全方面的代码
       //...
}
双亲委派模型

双亲委派模型是指当我们调用类加载器的loadClass方法进行类加载时,该类加载器会首先请求它的父类加载器进行加载,依次递归。如果所有父类加载器都加载失败,则当前类加载器自己进行加载操作。
逻辑很简单,通过ClassLoader类的源码来分析一下。

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        //进行类加载操作时首先要加锁,避免并发加载
        synchronized (getClassLoadingLock(name)) {
            //首先判断指定类是否已经被加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        //如果当前类没有被加载且父类加载器不为null,则请求父类加载器进行加载操作
                        c = parent.loadClass(name, false);
                    } else {
                       //如果当前类没有被加载且父类加载器为null,则请求根类加载器进行加载操作
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }

                if (c == null) {
                    long t1 = System.nanoTime();
                    //如果父类加载器加载失败,则由当前类加载器进行加载,
                    c = findClass(name);

                    //进行一些统计操作
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            //初始化该类
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

双亲委派模型的实现逻辑总体看还是非常简单明了的。
这里有几个细节需要说明:

  1. ClassLoader类是一个抽象类,但却没有包含任何抽象方法。
  2. 如果要实现自己的类加载器且不破坏双亲委派模型,只需要继承ClassLoader类并重写findClass方法。
  3. 如果要实现自己的类加载器且破坏双亲委派模型,则需要继承ClassLoader类并重写loadClass,findClass方法。
令人疑惑的系统类加载器

当你把上面的知识都搞清楚以后,会发现ClassLoader类中有个方法是getSystemClassLoader,系统类加载器,这又是什么?
系统类加载器是个容易让人混淆的概念,我一度以为它就是应用类加载器的别名,就跟启动类加载器和根类加载器道理一样。事实上,默认情况下我们通过ClassLoader.getSystemClassLoader()获取到的系统类加载器也确实是应用类加载器。
很多资料在说类加载器结构的时候会直接把应用类加载器说成是系统类加载器,其实我们通过类名就可以判断两个不是一回事。
系统类加载器可以通过System.setProperty("java.system.class.loader", xxx类名)进行自定义设置。
系统类加载器不是一个全新的加载器,它只是一个概念,本质上还是上述说的四大类加载器(把用户自定义类加载器算进去),至于提出这个概念的原因以及使用场景,还需要继续考究。

被人忽略的上下文类加载器

上面讨论了各个类加载器的加载路径。鉴于双亲委派模型的设计,子类加载器都保留了父类加载器的引用,也就是说当由子类加载器加载的类需要访问由父类加载器加载的类时,毫无疑问是可以访问到的。但考虑一种场景,会不会有父类加载器加载的类需要访问子类加载器加载的类这种情况?如果有,怎么解决(父类加载器并没有子类加载器的引用)?
这就是我们要讨论的常常被人们忽略的上下文类加载器。
经典案例:
JDBC是Java制定的一套访问数据库的标准接口,它包含在Java基础类库中,也就是说它是由根类加载器加载的。与此同时,各个数据库厂商会各自实现这套接口来让Java工程师可以访问自己的数据库,而这部分实现类库是需要Java工程师在工程中作为一个第三方依赖引入使用的,也就是说这部分实现类库是由应用类加载器进行加载的。
先上一段Java获取Mysql连接的代码:

//加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//连接数据库
Connection conn = DriverManager.getConnection(url, user, password);

这里DriverManager类就属于Java基础类库,由根类加载器加载。我们可以通过它获取到数据库的连接,显然是它通过com.mysql.jdbc.Driver驱动成功连接到了数据库,上面也说了数据库驱动(作为第三方类库引入)是由应用类加载器加载的。这个场景就是典型的由父类加载器加载的类需要访问由子类加载器加载的类。
Java是怎么实现这种逆向访问的呢?直接看DriverManager类的源码:

//建立数据库连接各个不同参数的方法最终都会走到这里
private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        //获取调用者的类加载器
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            //如果为null,则使用上下文类加载器
            //这里是重点,什么时候类加载器才会为null? 当然就是由根类加载器加载的类了
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }

        //...省略

        for(DriverInfo aDriver : registeredDrivers) {
            //使用上下文类加载器去加载驱动
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    //如果加载成功,则进行连接
                    Connection con = aDriver.driver.connect(url, info);
                    //...
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }
            } 
            //...
        }
    }

重点说明:
为什么上下文类加载器就可以加载到数据库驱动呢?回到上面一开始Launcher初始化类加载器的源码,我们发现原来所谓的上下文类加载器本质上就是应用类加载器,有没有豁然开朗的感觉?上下文类加载器只是为了解决类的逆向访问提出来的一个概念,并不是一个全新的类加载器,它本质上就是应用类加载器。


基本上我理解的Java类加载器就这么多知识,如果有没提到的或者是错误的地方,欢迎交流。

 

Java学习交流QQ群:523047986  禁止闲聊,非喜勿进!

java.sql.SQLIntegrityConstraintViolationException: #23000 java.sql.SQLIntegrityConstraintViolationException: #23000**异常信息****异常原因****解决方法** 异常信息 ### Cause: java.sql.SQLIntegrityConstraintViolationException: #23000 ; #23000; nested exception is java.sql.SQLIntegrityConstraintViolationException: #23000] with root c 阅读详情

相关推荐

解决mmdet3d安装中因PyTorch高版本引发的THC.h缺失问题

本文详细分析了在安装mmdet3d时,因PyTorch版本过高导致THC.h头文件缺失的常见报错问题。文章指出该错误的本质是PyTorch 1.7+版本废弃了旧API,与老版本C++扩展代码不兼容。提供了两种核心解决方案:一是降级PyTorch至1.6及以下版本;二是修改源码,将THC.h引用替换为ATen/cuda等新头文件,并给出了具体的代码修改示例和排查技巧。

weixin_29085151的博客 379

JVM之类加载器

负责加载class文件,class文件在文件开头有棒定的文件标示, 将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定 echo %JAVA_HOME% echo %PATH% echo %CLASSPATH% 类加载器分为两种,一种是虚拟机自带的类加载器,另一种是自定义的类加载器。我们通常不会自定义类加载器,而是使用java自带的类加载器,但是在框架、服务

沈光阳的博客 270

飞书多维表格+影刀RPA:我如何用3个指令搞定每周的销售数据自动汇总(附完整配置截图)

本文详细介绍了如何利用飞书多维表格和影刀RPA实现销售数据的自动化汇总,通过三个核心指令(建立链接、列出记录、更新记录)简化繁琐的手工操作。文章包含完整的配置截图和实战指南,帮助用户快速部署自动化流程,提升工作效率。

weixin_29273849的博客 284

Linux java编译报错找不到或无法加载主类

我发现的问题是 package com.example.canvas; public class LinuxDemo { public static void main(String[] args) { System.out.println("==============="); } }

红桃∩ 941

PADS VX2.7学习记录06-PADS Router软件功能操作

PADS VX2.7学习记录06-PADS Router软件功能操作

qq_31444421的博客 7860

【一步到位】Android Studio 实现安卓连接阿里云物联网云平台

最新版本目前是Android Studio Iguana | 2024.1.2,你可以到官网上去找之前的版本,手上有工作项目的不要去轻易尝试,说不定你就编译失败了,失败了也不要担心,解决就可以了。

m0_73799445的博客 1万+

linux下编译执行java类

--编译javac -cp commons-httpclient-3.1.jar:commons-logging-1.1.1.jar:commons-codec-1.6.jar:ojdbc14.jar: SendHibernateByShell.java--运行java -cp commons-httpclient-3.1.jar:commons-logging-1.1.1.jar:commons...

张林旺的专栏 2639

亲友圈棋牌的逆向工程_棋牌透视分析研究(一)

透视也能实现,有些台子就自带控制,这种东西权限只是存在于平台运营者手中,即便逆向破解也很难发现,但也有那么一些棋牌 比如斗牛或者跑得快,会先发给你的内存中存放当前的牌的信息,这样你就可以知道对面的牌是什么,但肯定也是加密了。明明是zip却无法解压,这很明显是属于被加密了, 那么我们就要开始找他的加密内容,一般加密的秘钥都会存储在so里面。写这一篇文章了,其实也想了很久,最终还是决定写了, 有些朋友找我做过菠菜的逆向,bjl,甚至是足球的!但很遗憾的告诉大家,这里是找不到的。

m0_63368535的博客 5803

linux下java虚拟机加载classloader动态类方法

将libclassloader.so文件放到jdk/jre/amd64下 32位jdk放到jdk/jre/i386下 tomcat配置catalina.sh  在Execute The Requested Command下面加上JAVA_OPTS='-agentlib:classloader' # ----- Execute The Requested Command -----------

牙刷也刷牙 1106

OpenClaw 从新手到中级完整教程【完整 持续更新版】

OpenClaw(曾用名 ClawdBot/Moltbot)是 2026 年最火的开源 AI Agent 项目,GitHub 已获得超过 68,000 星标。它不是普通的聊天机器人,而是一个真正能够执行任务的个人 AI 助理。

FeelTouch Labs 3113

【PySide6拓展】初识QChart图表

在 PySide6 中,QtCharts模块提供了一系列强大的类,用于创建和管理图表。QChart是这个模块的核心类,它可以帮助开发者轻松地实现数据的可视化。本篇文章将介绍如何使用 PySide6 的QtCharts模块,并通过一个简单的示例教你如何快速引入QChart。QChart折线图柱状图饼图区域图它能够直观地呈现数据,同时支持交互功能,如缩放、拖拽和工具提示等。通过引入QChart,我们可以轻松实现数据的可视化,提升应用的交互性和用户体验。结合其他QtCharts。

m0_62599305的博客 820

JVM-彻底理解类加载器

概念 顾名思义:类:指的是我们写的代码编译后产生的class文件。加载:就是把class文件存到内存中去。 加载过程 加载 -【验证-准备-解析】- 初始化 三种类型 BootStrapClassLoader 启动类加载器 启动类加载器主要加载的是JVM自身需要的类,这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将 <JAVA_HOME>/lib路径下的核...

leejiliang的博客 371

华为OD机试 - 开源项目热度榜单(Java 2025 C卷 100分)

H = Wwatch X#watch + Wstar X#star +Wfork X#fork +Wissue X#issue +Wmr X#mr H表示热度值,Wwatch、Wstar、Wfork、Wissue、Wmr分别表示5个统计维度的权重,#watch、#star、#fork、#issue、#mr分别表示5个统计维度的统计值。

学Java,找哪吒 1755

java 类加载器有几种_一篇文章读懂Java类加载器

原标题:一篇文章读懂Java类加载器 Java类加载器算是一个老生常谈的问题,大多Java工程师也都对其中的知识点倒背如流,最近在看源码的时候发现有一些细节的地方理解还是比较模糊,正好写一篇文章梳理一下。关于Java类加载器的知识,网上一搜一大片,我自己也看过很多文档,博客。资料虽然很多,但还是希望通过本文尽量写出一些自己的理解,自己的东西。如果只是重复别人写的内容那就失去写作的意义了。 类加载器...

weixin_36205186的博客 224

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

java类加载器 架构 设计_一篇文章读懂Java类加载器

Java类加载器算是一个老生常谈的问题,大多Java工程师也都对其中的知识点倒背如流,最近在看源码的时候发现有一些细节的地方理解还是比较模糊,正好写一篇文章梳理一下。关于Java类加载器的知识,网上一搜一大片,我自己也看过很多文档,博客。资料虽然很多,但还是希望通过本文尽量写出一些自己的理解,自己的东西。如果只是重复别人写的内容那就失去写作的意义了。类加载器结构类加载器结构名称解释:根类加载器,也...

weixin_32540087的博客 175

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Java多线程详解,一篇文章读懂多线程。

文章目录1. 基本概念2. 线程的创建和启动2.1. 多线程实现的原理2.2.多线程的创建,方式一:继承于Thread类2.3.多线程的创建,方式一:创建Thread匿名子类(也属于方法一)2.4. 多线程的创建,方式二:实现Runnable接口2.4.1. 比较创建线程的两种方式2.5. 多线程的创建,方式三:实现Callable接口2.6. 多线程的创建,方式四:线程池3. Thread类的常用方法4. 线程的调度4.1. cpu的调度策略4.2. Java的调度算法:5. 线程的生命周期6. 线程的同

brokyz 770

java类卸载机制_一文读懂Java中类的卸载机制

一文读懂Java中类的卸载机制一文读懂Java中类的卸载机制先下结论:使用JVM自带的类加载器(根加载器、扩展类加载器、系统类加载器)加载的类永远不会被卸载,使用自己创建的类加载器才会可能被卸载。至于为什么接下来就来一起研究吧。当一个类被加载、连接和初始化后,它的生命周期就开始了。当代表某个类的Class对象不再被引用,即不可达时,Class对象就会结束生命周期,该类在方法区内的数据也会被卸载,从...

weixin_39886238的博客 415

一文读懂Java中类的卸载机制

先下结论:使用JVM自带的类加载器(根加载器、扩展类加载器、系统类加载器)加载的类永远不会被卸载,使用自己创建的类加载器才会可能被卸载。 至于为什么接下来就来一起研究吧。 类的生命周期 当一个类被加载、连接和初始化后,它的生命周期就开始了。 当代表某个类的Class对象不再被引用,即不可达时,Class对象就会结束生命周期,该类在方法区内的数据也会被卸载,从而结束该类的生命周期。 由此可见,一个类何时结束生命周期,取决于代表它的Class对象何时结束生命周期。 Class对象的引用关系 那么Class对象什

阳阳的博客 1777

【面试】一文读懂Java类加载全过程

Java类加载过程是Java虚拟机(JVM)将.class文件中的字节码装载到内存中,并对字节码进行验证、准备和初始化的过程。这个过程涉及到了Java虚拟机的类加载器、运行时数据区等多个方面,其中包含了很多的细节和技术问题。A: 类加载是将Java字节码文件加载到JVM中,并在运行期间动态生成Java类的过程。A: 类加载器的命名空间是指每个类加载器所加载的类都拥有独立的命名空间,相同名称的类可以存在于不同的命名空间中。

DreamSun的博客 782

一篇文章丢给你,让你一文了解JVM

又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考以下是部分内容截图《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!” />

2401_84048895的博客 566

一篇JVM文章,让你读懂对象的一生

如果一个类加载器收到了加载类的请求,首先不会自己尝试去加载这个类,而是向上请求父类加载器去完成,每一个加载器都是如此工作,一直到最顶层的启动类加载器。从启动类加载器开始,只有当父类无法加载这个请求的,便会将请求发送到子类,子类尝试加载。启动类加载器:加载 JDK 中 lib 目录中 Java 的核心类库,即$JAVA_HOME/lib目录。扩展类加载器。加载 lib/ext 目录下的类。应用程序类加载器:加载我们写的应用程序。自定义类加载器:根据自己的需求定制类加载器。

qq_53223625的博客 713

JVM的艺术—类加载器篇(三)

JVM的艺术—类加载器篇(三) 引言 今天我们继续来深入的剖析类加载器的内容。上篇文章我们讲解了类加载器的双亲委托模型、全盘委托机制、以及类加载器双亲委托模型的优点、缺点等内容,没看过的小伙伴请加关注。在公众号内可以找到,jvm的艺术连载篇。欢迎各位小伙伴儿的持续关注,同时也感谢各位读者一直以来的支持,本人会一直坚持原创、独立创作,给各位读者带来真正的、实用的干货。也会把文章写的通俗易懂,从人的思维、从程序员的思维中,不断的改善写作技巧。争取让每个人都能花最少的学习成本,读懂最好的文章。谢谢。 由于被一些私

雕爷的架构之路 353

一文读懂java的双亲委派机制

类加载器采用了双亲委派模型来保证类的加载安全和一致性

m0_58395170的博客 282
上一篇: html5学习之旅第一篇
下一篇: C# 面向对象之类和方法
linux12a
博客等级 码龄9年 20粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值