惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
有赞技术团队
有赞技术团队
博客园_首页
IT之家
IT之家
爱范儿
爱范儿
量子位
小众软件
小众软件
Jina AI
Jina AI
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 聂微东
The Cloudflare Blog
博客园 - 司徒正美
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
V2EX
大猫的无限游戏
大猫的无限游戏
月光博客
月光博客
雷峰网
雷峰网
V
Visual Studio Blog
博客园 - Franky
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
美团技术团队
Last Week in AI
Last Week in AI
S
SegmentFault 最新的问题

Nicksxs's Blog

Pi Agent 源码解析(一):从调试环境到双层主循环 6GB显存能跑35B MoE吗:FreeToken极限配置、性能压榨与实操教程 聊聊 Java 的类加载机制一 从ASM到ByteKit再到Arthas:一条Java字节码增强链路 8GB显存如何跑35B:深入理解FreeToken的专家缓存与CPU-GPU协同 让chatgpt来给我讲解下deepseek harness的插件体系 来学习下pi agent的原理 如何在命令行中输出进度条 联想 G400 安装 Windows 7 折腾记录 记一个gitea推送失败的问题 学一下chrome的扩展开发 看下chrome的内置模型 从 app.test 到小锁:valet 本地 HTTPS 的完整链路 浅析一下jpeg图片格式及其来源 关于github拉取下载加速的另一个方式 关于适合什么模型,推荐下llmfit 看看目前本地能跑什么模型,使用llama.cpp 最近使用vibe coding的一些感悟 使用php的inotify扩展来监听文件变更 一些设计模式的记忆点 使用xiaomi mimo大模型api运行Hermes Agent 结合Obsidian的cli的一体化体验 开始尝试使用obsidian作为笔记软件 学习下大神的知识库 体验下微软开源的Markdown转换工具Markitdown 学习下git的worktree 一些架构师知识点的记录 解答一下关于traefik的一点疑惑 记录一下迁移服务器需要使用的一些命令 较早代iPhone更换新iPhone的一些小指南
重新复习理解下java的类加载器
Nicksxs · 2025-12-28 · via Nicksxs's Blog

之前比较粗浅的写过一点类加载器的相关知识,最近因为在看相关的内容,所以打算来复习也简单分享下
首先是我们常规用户自定义类的类加载器,

1
2
3
4
public class ClassLoaderDemo {
public static void main(String[] args) {
ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();
System.out.println(classLoader);

输出的是

1
sun.misc.Launcher$AppClassLoader@18b4aac2

是这个AppClassLoader
之前也讲到过,常规的层级就是
用户自定义类加载器 -> AppClassLoader -> ExtClassLoader -> BootstrapClassLoader
可以用一段简单的代码来看下

1
2
3
4
5
6
7
8
9
10
11
StringBuilder sb = new StringBuilder("|--");
boolean needContinue = true;
while (needContinue) {
System.out.println(sb.toString() + classLoader);
if (classLoader == null) {
needContinue = false;
} else {
classLoader = classLoader.getParent();
sb.insert(0, "\t");
}
}

可以看到

1
2
3
|--sun.misc.Launcher$AppClassLoader@18b4aac2
|--sun.misc.Launcher$ExtClassLoader@555590
|--null

最后的就是 BootstrapClassLoader,因为是native实现的,所以没有类名
那么到这呢,就是想接着说下双亲委派,这个翻译可能跟实际的理解不一定一样属于见仁见智
个人的观点主要是第一是保证了一致性,不会随意的使用classloader,对于比如jdk提供的类,应该由什么类加载器来加载是稳定可预期的,另一个就是各司其职,
我们可以看下BootstrapClassLoader类加载器主要负责加载哪些类

1
2
3
4
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL url : urls) {
System.out.println(url.toExternalForm());
}

主要是核心的这些包

1
2
3
4
5
6
7
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/classes

而ExtClassLoader
则是负责 /jre/lib/ext 下面的类
因为没有公开方法
我们捞一下
其实代码都在 sun.misc.Launcher 里面
创建的时候

1
2
3
4
5
private static ExtClassLoader createExtClassLoader() throws IOException {
try {
return (ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<ExtClassLoader>() {
public ExtClassLoader run() throws IOException {
File[] var1 = Launcher.ExtClassLoader.getExtDirs();

而这个 getExtDirs() 就是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static File[] getExtDirs() {
String var0 = System.getProperty("java.ext.dirs");
File[] var1;
if (var0 != null) {
StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
int var3 = var2.countTokens();
var1 = new File[var3];

for(int var4 = 0; var4 < var3; ++var4) {
var1[var4] = new File(var2.nextToken());
}
} else {
var1 = new File[0];
}

return var1;
}

打印出来就是
/Users/user/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
在这些目录下的

AppClassLoader
则是当前应用程序的classpath下的类
这里就有很多了,不过classpath好像也是个知识点,可以后面讲讲,这块主要还是之前也提过,首先是找这个类的实例化对象,找到了就可以没有就调用父级的loadClass,实现逐层找,找不到在通过本加载器的findClass去加载。这里可以稍微想一下,如果有父类加载器就让parent加载,parent也是再往上,当这个parent是比如Ext时,委托它的parent就是Bootstrap,加载为空时,就会走到第二个if (c==null)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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) {
c = parent.loadClass(name, false);
} else {
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;
}
}

然后调用当前类加载器的findClass进行处理,这里还是异常就是对下层来说的 c == null 会往下再调用 findClass,这样就完成了往上找再往下逐层尝试加载