FanControl传感器异常诊断与修复:从问题识别到彻底解决的完整指南
2026/4/20 13:16:21
内容来自JVMS Chapter 5 – Loading, Linking, and Initialization(类加载、链接、初始化),这是 JVM 规范中对类加载的正式描述。这不是教材版,而是接近 JVM 规范原文的专业总结版。
JVM 不定义类加载器的层次结构、不强制双亲委派,也不定义 classpath。 它只要求:类必须通过某个加载器被定义,类的身份由“类名 + 加载器”唯一确定。 加载、链接、初始化的每个环节必须按规范精确执行。
Loading → Linking → Initialization加载 → 链接 → 初始化
JVMS 定义类处理流程分为三大阶段:
1. Loading(加载) 2. Linking(链接) 2.1 Verification(验证) 2.2 Preparation(准备) 2.3 Resolution(解析) 3. Initialization(初始化)类加载只是整个过程的第一步,不包含解析、初始化。
JVMS 定义:
Class loading is the process of locating a binary representation of a class or interface and transforming it into the runtime data structure inside the JVM.
类加载 = 找到类的二进制表示,并创建其在 JVM 中的运行时表示。
规范强调三点:
可以来自:.class/JAR/网络/动态生成的字节数组(如 ASM/CGLIB)/数据库/加密文件
JVM 标准不限制来源。
规范中称之为:defineClass findClass
规范描述为:runtime representation of classes 类的运行时表示
类加载器不是 Java 语言的组成部分
但 JVM 规范要求:
The Java Virtual Machine must maintain a mapping between class loaders and the classes they have loaded.Java
虚拟机必须维护类加载器与其加载的类之间的映射关系。
这意味着:
最关键的一段:
The Java Virtual Machine specification does not require class loaders to follow a parent-child hierarchy.Java
虚拟机规范不要求类加载器遵循父子层次结构。
也就是说:
非常重要的一段:
The delegation model is strongly recommended but not strictly required by this specification.
本规范强烈建议采用委托模型,但并非严格要求。
这意味着:
规范定义类的唯一标识为:
Class Identity = (class name + defining class loader)因此:
注意:这里两个不同的ClassLoader可以是不同类型的ClassLoader,也可以是同类型ClassLoader的不同实例
不同类型的ClassLoader:
// 类型1:JDK 自带的 URLClassLoader
URL url = classPath.toUri().toURL();
URLClassLoader urlLoader = new URLClassLoader(new URL[]{url}, null);
// 类型2:我们自己写的 MyFileClassLoader(完全不同类型!)
MyFileClassLoader myLoader = new MyFileClassLoader(classPath, null);
// 加载同一个类
Class<?> c1 = urlLoader.loadClass("a7.Person");
Class<?> c2 = myLoader.loadClass("a7.Person");
不同实例:
// 两个完全一样的 class 文件,但路径不同 → 两个不同的 ClassLoader
URL url1 = Paths.get("D:/MyWork/0-create/practice idea/jvm/jvm-practice/out/production/part4/").toUri().toURL();
URL url2 = Paths.get("D:/MyWork/0-create/practice idea/jvm/jvm-practice/out/production/part4/").toUri().toURL();
// 父加载器都设为 null(直属 Bootstrap),这样更纯粹,也可以设为 getSystemClassLoader()
URLClassLoader loader1 = new URLClassLoader(new URL[]{url1}, null);
URLClassLoader loader2 = new URLClassLoader(new URL[]{url2}, null);
// 加载同一个全限定名的类
Class<?> clazz1 = loader1.loadClass("a7.v1.User");
Class<?> clazz2 = loader2.loadClass("a7.v2.User");
这是 OSGi、Tomcat 多版本隔离的理论基础。
规范要求,类只有在首次主动使用时才必须加载:
主动使用包括:
JVM 必须保证:
规范要求 defineClass 只做:
它不负责:
这些都是 Java 层的 ClassLoader 负责的。
自定义 ClassLoader从 JVM 标准的角度完全合法,只要保证:
下面是一个打破双亲委派的例子(基于jdk17):
import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; public class MyClassLoaderJdk17 extends ClassLoader { private final String classPath; private final String targetPackagePrefix = "com.test"; public MyClassLoaderJdk17(String classPath) { super(MyClassLoaderJdk17.class.getClassLoader()); // parent = app loader this.classPath = classPath; } @Override public Class<?> loadClass(String name) throws ClassNotFoundException { // JPMS 下绝不能尝试加载 java.*,否则报错 if (name.startsWith("java.") || name.startsWith("jdk.") || name.startsWith("javax.")) { return super.loadClass(name); } // 对业务包破坏双亲委派 if (name.startsWith(targetPackagePrefix)) { synchronized (getClassLoadingLock(name)) { Class<?> loaded = findLoadedClass(name); if (loaded != null) return loaded; try { return findClass(name); } catch (Exception ignored) { } } } // 其余保持默认委派 return super.loadClass(name); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { String file = classPath + "/" + name.replace('.', '/') + ".class"; byte[] bytes = Files.readAllBytes(Paths.get(file)); return defineClass(name, bytes, 0, bytes.length); } catch (IOException e) { throw new ClassNotFoundException(name, e); } } }因此:
完全符合 JVM 标准。