怎么做免费网站推广,wordpress评论过滤,网页制作报价单,discuz可以做商城网站吗JVM 基础入门
JVM 基础
聊一聊 Java 从编码到执行到底是一个怎么样的过程#xff1f; 假设我们有一个文件 x.Java#xff0c;你执行 javac#xff0c;它就会变成 x.class。
这个 class 怎么执行的#xff1f;
当我们调用 Java 命令的时候#xff0c;class 会被 load 到…JVM 基础入门
JVM 基础
聊一聊 Java 从编码到执行到底是一个怎么样的过程 假设我们有一个文件 x.Java你执行 javac它就会变成 x.class。
这个 class 怎么执行的
当我们调用 Java 命令的时候class 会被 load 到内存这块叫【Classloader】会被 Classloader 装载到内存里。
一般的情况下我们写自己的类文件的时候也会用到 【Java 的类库】所以他会把 Java 类库相关的这些个类也要装载到内存里装载完成之后会调用【字节码解释器】或者是【JIT 即时编译器】来进行解释或编译编译完之后由【执行引擎】开始执行这以及下面面对的那就是操作系统和硬件了。
Java 编译好了之后变成 class class 会被 load 到内存与此同时像什么 string object 这些个 class 也都会被 load 到内存。
Java 是这个解释执行的还是编译执行的
其实解释和编译是可以混合的特别常用的一些代码代码用到的次数特别多这个时候他会把代码做成一个及时的编译做成一个本地的编译。
你可以理解为就像 c 语言在 Windows 上执行的时候把它编译成 exe 一样那么下次再执行这段代码的时候就不需要通过解释器来一句一句解释来执行了执行引擎可以直接交给操作系统去让它调用这个效率要高很多不是所有的代码都要都会被 GIT 进行及时编译的如果是这样的话那整个 Java 就完全变成了不能跨平台了。
所以有一些特定的执行起来执行次数好多好多用的特别多的时候这个时候会进行一个即时编译器的编译。
什么是 JVM?
所谓的 JVM 虚拟机其实它本身是一个规范是虚构出来的一台计算机。拥有自己的操作系统是一个跨语言平台。
为什么 JVM 虚拟机能够支持多种语言运行在上面呢
最关键的原因是就是因为 class 这个东西我们可以说任何的语言只要你能编译成 class符合 class 文件的规范你就可以扔在 Java 虚拟机上去执行。
注意JVM 只和 class 文件有关与 java 无关。
JDK 官网Java SE Specifications (oracle.com)
维基百科Java虚拟机 - 维基百科自由的百科全书 (wikipedia.org)
甲骨文中国Java 软件 | Oracle 中国
常见的 JVM 实现 Hotspot最常用 oracle 官方我们做实验用的 JVMJava 虚拟机java -version 命令可查看使用的是什么 JVMHotspot 8 之后要收费但 Open JDK 是开源的版本免费 Jrockit BEA 曾经号称世界上最快的 JVM被 Oracle 收购合并于 hotspot TaobaoVM免费 hotspot 深度定制版 LiquidVM 直接针对硬件 azul zing特别贵 最新垃圾回收的业界标杆号称 1ms 以内www.azul.com J9-IBM Microsoft VM
JDK JRE JVM 的关系
包含关系。
JVM – 运行 java 字节码的虚拟机
JRE – java 运行环境 jvm core核心类库
JDK – java 开发工具包 jre development kit开发工具
Class 文件格式
Class 文件格式File Format
二进制字节流由 java 虚拟机解释数据类型: u1 u2 u4 u8 和 _info (表类型) info 的来源是 hotspot 源码中的写法 查看 16 进制格式的 ClassFile 软件 - sublime / notepad /IDEA 插件 - BinEd 有很多可以更好观察 ByteCode 的方法 终端命令javap class文件路径-v 参数详细查看JBE 可以直接修改JClassLib - IDEA 插件之一 文件结构 General Information通用信息 这个部分包含了一些通用的信息比如文件的魔数magic number文件版本号等。Constant Pool常量池 常量池是一个重要的数据结构它存储了一系列常量包括字面值、符号引用、类和接口的名称等。常量池在字节码文件中起到了存储和管理常量数据的作用。Interfaces接口 这部分定义了该类实现的接口。Fields字段 这部分描述了类的字段成员变量包括字段的名称、类型以及修饰符等信息。Methods方法 这部分描述了类的方法包括方法的名称、参数列表、返回类型、字节码指令等。Attributes属性 属性提供了关于类、字段或方法的其他信息例如源代码行号、注解等。这个部分可以包含多个不同类型的属性每个属性都有一个名称和相应的数据。
如下图示 magic魔数是一个固定的字节序列用于标识 Java 字节码文件。它的值为 0xCAFEBABE用于确定文件是否是有效的 Java 字节码文件。minor version次版本号指示 Java 编译器版本的次要更新。用于描述字节码文件与Java虚拟机版本的兼容性。major version主版本号指示 Java 编译器版本的主要更新。同样用于描述字节码文件与 Java 虚拟机版本的兼容性。constant_pool_count常量池计数表示常量池中常量的数量。十六进制 0010 转化为十进制为 16。常量池真正存储内容的时候存了 16 -1 项。后面的内容都是引用它。#1:常量池项通常以 # 开头表示一个常量池中的单个项。access flags访问标志描述类或接口的访问级别和特性。this_class当前类的索引指示当前类在常量池中的位置。super class父类的索引指示父类在常量池中的位置。interface_count接口数量表示该类实现的接口数量。interfaces接口列表描述该类实现的接口在常量池中的位置。fields_count字段数量表示该类中声明的字段数量。methods_count方法数量表示该类中声明的方法数量。method_info方法信息描述方法的访问标志、方法名、参数列表等。attribute_count属性数量表示该类的属性数量。
每个部分描述了 Java 字节码文件的不同方面从类的声明到方法的定义以及与常量池等相关的信息。这些信息在 Java 虚拟机中被解析和使用以正确地加载和执行 Java 类。 Java 的汇编指令有 200 多条。 好文分享class类文件结构 类加载 - 初始化
加载过程
加载、链接和初始化是 Java 程序运行时的三个主要阶段。 Loading – 加载 Linking – 链接 Verification – 验证Preparation – 准备Resolution – 解析 Initializing – 初始化
具体来说 Loading加载 这是类加载过程的第一个阶段。在这个阶段Java 虚拟机JVM会从类的外部源加载类的二进制数据通常是从磁盘文件中加载但也可以是网络、内存等。 加载器将类的二进制数据class文件从外部源加载到内存中并将其放置在运行时数据区的方法区内。 Linking链接 这是加载过程的第二个阶段它将类的二进制数据链接到 JVM 的运行时状态。 链接过程分为以下三个步骤 Verification验证确保类的二进制数据符合 JVM 规范不违反类加载的安全性要求。这个步骤确保类的结构正确不会引发运行时错误。Preparation准备为类的静态变量分配内存空间并设置默认的初始值通常为零值。这个步骤为类变量分配内存并初始化但不会为实例变量分配内存。把 class 文件的静态变量赋默认值比如 int 类型的默认值为 0Resolution解析将class文件里面的常量池里面用到的符号引用转换为直接引用解析动作可以在运行时再完成也可以在编译时静态完成。这个步骤处理类、接口、方法和字段的符号引用将其解析为实际的引用。 Initializing初始化 这是链接过程的最后一个阶段也是类加载的最终阶段。在这个阶段类的静态初始化器会被执行初始化静态字段和执行静态块。这个阶段是在类被首次使用时触发的例如创建类的实例、访问类的静态字段等。调用类初始化代码 clinit给静态成员变量赋初始值
总结来说类加载过程涉及到从外部源加载类的二进制数据然后将其链接到 JVM 的运行时状态最终进行初始化。链接阶段包括验证、准备和解析而初始化阶段则会执行类的静态初始化器。这个过程确保类在 Java 虚拟机中正确加载和使用。
加载之后会发生什么
任何一个 class 被加载到内存之后会生成了两块内容。
第一块内容是这个二进制的这块东西确实被落到内存放到了内存的一块区域可以原封不动的扔进去。第二个它生成了一个 class 类的对象通过以后其他的那些我们自己写的对象去访问这个对象通过这个对象去访问 class 类的文件所以生成一个 class 的对象这个 class 对象是指向了这块内容。
在 Java 虚拟机中类的元数据包括类的结构信息、字段、方法、父类、接口等都会被加载到方法区元空间Metaspace替代了永久代PermGen中。Class 对象本身也是类的元数据之一因此 Class 对象也存放在方法区中。 Class 对象在内存中的位置可以看作是类的描述符用来操作该类的字节码以及其他相关的元数据。
可以看看下面这两篇文章
方法区与MetaspaceJava8取消permgen使用mataspace有什么好处内存结构有什么本质的变化
类加载器
JVM 它本身有一个类加载器的层次这个类加载器就是一个普通的 classJVM 有一个类加载器的层次分别来加载不同的 class。或者说JVM 里面所有的 class 都是被类加载器给加载到内存的那么这个类加载器简单说我们可以把它叫做 ClassLoader。 注意从下往上是委托给父加载器不是继承关系是语法上的一种关系。
在 Java 虚拟机中类加载器按照层次结构进行组织分为三个主要层次启动类加载器Bootstrap ClassLoader、扩展类加载器Extension ClassLoader和应用程序类加载器Application ClassLoader也称为系统类加载器。这些类加载器形成了类加载器的双亲委派模型。
启动类加载器Bootstrap ClassLoader这是 Java 虚拟机的一部分用于加载 Java 核心库例如 java.lang 包中的类。它是所有其他类加载器的父加载器但它本身不是一个普通的 Java 类因此在源码中并没有对应的类。它位于虚拟机内部通常用本地代码实现。它是最顶层的类加载器负责加载 Java 核心类库。扩展类加载器Extension ClassLoader 扩展类加载器用于加载 Java 虚拟机的扩展类库位于 jre/lib/ext 目录中的类。它的父加载器是启动类加载器。应用程序类加载器Application ClassLoader这个加载器也称为系统类加载器它负责加载应用程序的类包括用户自定义的类和第三方库中的类。它的父加载器是扩展类加载器。
启动类加载器、扩展类加载器和应用程序类加载器构成了类加载器的层次结构通过双亲委派模型来保证类的加载的一致性和安全性。在加载一个类时首先会尝试由父加载器加载只有在父加载器无法加载时子加载器才会尝试加载。这个模型可以防止类的重复加载同时保证了类的隔离性。
在启动类加载器加载的类中有一部分是虚拟机内部的类比如 java.lang.Object、java.lang.String 等。这些类并不是普通的 Java 类因此没有对应的源码。而在 Java 虚拟机的源码中通常会对这些类的加载过程进行描述但实际上它们是由虚拟机的实现提供的。
最顶层加载器 Bootstrap 会返回一个空值。
public class T004_ParentAndChild {public static void main(String[] args) {System.out.println(T004_ParentAndChild.class.getClassLoader());System.out.println(T004_ParentAndChild.class.getClassLoader().getClass().getClassLoader()); // AppSystem.out.println(T004_ParentAndChild.class.getClassLoader().getParent()); // ExtensionSystem.out.println(T004_ParentAndChild.class.getClassLoader().getParent().getParent()); // Bootstrap返回 null// System.out.println(T004_ParentAndChild.class.getClassLoader().getParent().getParent().getParent()); // 解开注释就会报空指针异常}
}输出结果
sun.misc.Launcher$AppClassLoader18b4aac2
null
sun.misc.Launcher$ExtClassLoader4554617c
null双亲委派
父加载器 父加载器不是【类加载器的加载器】也不是【类加载器的父类加载器】 双亲委派是一个孩子向父亲方向然后父亲向孩子方向的双亲委派过程思考为什么要搞双亲委派 java.lang.String 类由自定义类加载器加载行不行 – 不行因为不安全
先是自底向上检查是否已经加载然后再回过头来找 class 并加载查看是否加载成功一直到底都没加载成功就会抛出一个 class 找不到异常。 这个缓存是在哪缓存
可以简单的认为是它自己内部维护的一个容器一个 list 或者一个数组加载的东西都扔在里面。每个加载器都有自己的缓存。
为什么类加载器要使用双亲委派重要
主要是为了安全。次要原因是资源浪费问题避免重新加载问题防止类重复加载。
类加载器范围
来自 Launcher 源码
启动类加载器 Bootstrap ClassLoader 加载路径sun.boot.class.path范围它负责加载 Java 核心类库这些类库包括 Java API 中的基础类如 java.lang 包下的类等。作用它是 Java 类加载器中最顶层的类加载器负责加载虚拟机自身需要的类以及在运行期间会被系统使用的类。由于 Bootstrap ClassLoader 是用本地代码来实现的所以在 Java 中无法直接获取到该类加载器的引用。 扩展类加载器 ExtensionClassLoader 加载路径java.ext.dirs范围它负责加载 Java 的扩展库这些库位于 JRE 的 lib/ext 目录或者由系统变量 java.ext.dirs 指定的路径。作用它负责加载一些 Java 标准扩展库以及一些自定义的扩展库这些库通常是一些可选的功能。扩展类加载器是由 sun.misc.Launcher$ExtClassLoader 实现的。 系统类加载器 AppClassLoader 加载路径java.class.path范围也被称为系统类加载器它负责加载用户类路径Classpath上指定的类。作用它负责加载应用程序中的类包括开发者自己编写的类以及引用的第三方类库。应用类加载器是由 sun.misc.Launcher$AppClassLoader 实现的。
代码查看
public class T003_ClassLoaderScope {public static void main(String[] args) {System.out.println(根目录下加载);String pathBoot System.getProperty(sun.boot.class.path);System.out.println(pathBoot.replaceAll(;, System.lineSeparator()));System.out.println(--------------------);System.out.println(ext 下加载);String pathExt System.getProperty(java.ext.dirs);System.out.println(pathExt.replaceAll(;, System.lineSeparator()));System.out.println(--------------------);System.out.println(App 下加载);String pathApp System.getProperty(java.class.path);System.out.println(pathApp.replaceAll(;, System.lineSeparator()));}
}输出结果
根目录下加载
C:\Program Files\Java\jdk1.8.0_321\jre\lib\resources.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\rt.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\sunrsasign.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\jsse.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\jce.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\charsets.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\jfr.jar
C:\Program Files\Java\jdk1.8.0_321\jre\classes
--------------------
ext 下加载
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext
C:\WINDOWS\Sun\Java\lib\ext
--------------------
App 下加载
C:\Program Files\Java\jdk1.8.0_321\jre\lib\charsets.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\deploy.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\access-bridge-64.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\cldrdata.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\dnsns.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\jaccess.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\jfxrt.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\localedata.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\nashorn.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\sunec.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\sunjce_provider.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\sunmscapi.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\sunpkcs11.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\ext\zipfs.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\javaws.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\jce.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\jfr.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\jfxswt.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\jsse.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\management-agent.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\plugin.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\resources.jar
C:\Program Files\Java\jdk1.8.0_321\jre\lib\rt.jar
D:\笔记\学习资料\马士兵\JVM视频源码\out\production\JVM // 项目路径
D:\software\idea2022\IntelliJ IDEA 2022.1.3\lib\idea_rt.jar自定义类加载器
当你想加载某个类的时候可以调用 loadClass 方法
public class T005_LoadClassByHand {public static void main(String[] args) throws ClassNotFoundException {Class clazz T005_LoadClassByHand.class.getClassLoader().loadClass(com.mashibing.jvm.c2_classloader.T002_ClassLoaderLevel);System.out.println(clazz.getName());//利用类加载器加载资源参考坦克图片的加载//T005_LoadClassByHand.class.getClassLoader().getResourceAsStream();}
}loadClass 源码解析
loadClass 方法是在双亲委派模型下实现的。
findInCache - parent.loadClass - findClass()
private final ClassLoader parent; // final 关键字修饰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) {// 如果找不到类抛出ClassNotFoundException// 从非空父类装入器}// 如果父加载器也找不到则尝试自己加载if (c null) {// 如果仍未找到则按顺序调用 findClass// to find the class. 要找到这个类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;}}protected Class? findClass(String name) throws ClassNotFoundException { // findClass被protected修饰保护起来throw new ClassNotFoundException(name);}主要步骤如下
首先方法会尝试从已加载的类中查找目标类是否已经加载如果已经加载则直接返回该类。如果目标类未加载它会尝试委派给父类加载器来加载。这是双亲委派模型的核心思想之一。父类加载器会重复这个过程直到到达启动类加载器Bootstrap ClassLoader为止。如果父类加载器也无法加载目标类则 loadClass 方法会调用 findClass 方法尝试自己加载目标类。这是子类加载器在加载自己类的最后一步。最后如果 resolve 参数为 true则会调用 resolveClass 方法用于解析加载的类确保类的完整性和正确性。
什么时候需要自己去加载
加载进去就生成 class 对象吗
不是要经过 初始化 才生成 class 对象。
自定义一个类加载器需要做什么
只需要做一件事就是定义自己的 findClass 就可以了。
具体来说 继承 ClassLoader 重写模板方法 findClass 调用 defineClass(byte[] - Class clazz) 加密可自定义类加载器加载自加密的 class 防止反编译防止篡改
代码如下
首先继承 ClassLoader 这个类
// 自定义类加载器继承自ClassLoader
public class T006_MSBClassLoader extends ClassLoader {// 重写findClass方法用于加载类字节码Overrideprotected Class? findClass(String name) throws ClassNotFoundException {// 根据类名构建文件路径File f new File(c:/test/, name.replace(., /).concat(.class));try {// 读取字节码文件FileInputStream fis new FileInputStream(f);ByteArrayOutputStream baos new ByteArrayOutputStream();int b 0;// 读取文件内容并写入字节数组输出流while ((bfis.read()) !0) {baos.write(b);}// 将字节数组转换为字节数组byte[] bytes baos.toByteArray();baos.close();fis.close(); // 关闭流// 使用defineClass方法定义并返回类return defineClass(name, bytes, 0, bytes.length);} catch (Exception e) {e.printStackTrace();}// 若加载失败则调用父类的findClass方法return super.findClass(name); // throws ClassNotFoundException}// 主函数public static void main(String[] args) throws Exception {// 创建自定义类加载器实例ClassLoader l new T006_MSBClassLoader();// 通过自定义类加载器加载类Class clazz l.loadClass(com.mashibing.jvm.Hello);Class clazz1 l.loadClass(com.mashibing.jvm.Hello);// 输出两次加载的类是否相同System.out.println(clazz clazz1);// 创建加载的类的实例并调用方法Hello h (Hello) clazz.newInstance();h.m();// 输出类加载器信息System.out.println(l.getClass().getClassLoader()); // 输出自定义类加载器的类加载器System.out.println(l.getParent()); // 输出自定义类加载器的父类加载器System.out.println(getSystemClassLoader()); // 输出系统类加载器}
}public class Hello {public void m() {System.out.println(Hello JVM!);}
}这段代码实现了一个自定义的类加载器 T006_MSBClassLoader该类加载器继承自 ClassLoader重写了 findClass 方法来加载类的字节码。在主函数中使用这个自定义的类加载器加载类并输出加载的类是否相同然后创建该类的实例并调用方法。最后输出了自定义类加载器的类加载器和父类加载器信息以及系统类加载器的信息。这种自定义类加载器的方式允许你从非标准的位置加载类文件并且可以通过不同的类加载器实现类的隔离。 defineClass 方法是 ClassLoader 类中的一个重要方法用于将字节数组转换为一个 Class 对象。该方法的作用是将一个【字节数组中的类字节码】转换为一个【Java 类的实例】。当一个类加载器调用 defineClass 方法时它会将字节数组中的类字节码转换为一个 Class 对象并返回该对象。(不会验证字节码的正确性) 输出结果 true
Hello JVM!
sun.misc.Launcher$AppClassLoader18b4aac2
sun.misc.Launcher$AppClassLoader18b4aac2
sun.misc.Launcher$AppClassLoader18b4aac2加密
大家都知道 java 的代码 class 文件很容易就被反编译了。
但是我要是定义自己的格式我不想让别人反编译这时候怎么办
你可以通过自定义的 class loader 来进行。然后在写逻辑的时候加一个加密操作。
编译器
三大模式
解释器bytecode intepreter – 解释模式JITJust In-Time compiler – 编译模式混合模式 混合使用解释器 热点代码编译编译成本地代码就不用在解释器里面解释来执行了效率提升起始阶段采用解释执行热点代码检测 多次被调用的方法(方法计数器:监测方法执行频率)多次被调用的循环(循环计数器:检测循环执行频率)进行编译
为什么不干脆直接编译成本地代码那执行效率不更高吗
有两个原因。
第一Java 的解释器现在它的效率也已经非常高了在一些简单代码的执行上它并不输于你编译成本地代码。第二如果你一个执行的文件特别多各种各样的类库的时候好几十个 class你上来二话不说先在内存里编译一遍这个启动过程会长得吓人。所以它现在默认的模式是混合模式。
可以用指明参数的方式指定用什么模式 -Xmixed 默认为混合模式 开始解释执行启动速度较快对热点代码实行检测和编译 -Xint 使用解释模式启动很快执行稍慢 -Xcomp 使用纯编译模式执行很快启动很慢要编译的类少的时候启动也会很快
懒加载
lazyloading
严格讲应该叫 lazylnitializingJVM 规范并没有规定何时加载但是严格规定了什么时候必须初始化五种情况 使用 newgetstaticputstaticinvokestatic 指令时访问 final 变量除外java.lang.reflect 对类进行反射调用时初始化子类的时候父类必须首先初始化虚拟机启动时被执行的主类必须初始化动态语言支持 java.lang.invoke.MethodHandle 解析的结果为 REF_getstatic, REF_putstatic, REF_invokestatic 的方法句柄时该类必须初始化
拓展知识
双亲委派如何打破
parent 是如何指定的打破双亲委派
用 super(parent) 指定双亲委派的打破 如何打破重写 loadClass() 方法何时打破过 JDK1.2 之前自定义 ClassLoader 都必须重写 loadClass()ThreadContextClassLoader 可以实现基础类调用实现类代码通过 thread.setContextClassLoader 指定热启动热部署 osgi tomcat 都有自己的模块指定 classloader可以加载同一类库的不同版本
类加载器涉及到了哪些设计模式
在 Java 虚拟机 (JVM) 中的类加载器 (ClassLoader) 实现中通常使用了以下两种设计模式
委托模式 (Delegation Pattern)ClassLoader 的实现通常使用了委托模式来处理类加载请求。委托模式指的是当一个对象收到请求时它将请求委托给其他对象来处理。在类加载器的情况下当一个类加载器接收到加载类的请求时它首先会将请求委托给父类加载器进行处理。如果父类加载器无法加载该类子类加载器才会尝试加载类。这种委托模式的设计方式可以实现类加载器的层次结构从而实现类加载器的隔离和类加载的委派。单一职责模式 (Single Responsibility Pattern)ClassLoader 的主要责任是加载类文件并定义对应的类。ClassLoader 的设计符合单一职责模式即一个类应该只负责一项职责。ClassLoader 将类加载的职责封装在一个独立的类中从而使得类加载的逻辑与其他功能解耦提高了代码的可维护性和可扩展性。
除了委托模式和单一职责模式ClassLoader 的实现可能还涉及其他设计模式具体取决于实际的实现细节和需求。例如一些类加载器的缓存机制可能使用了享元模式 (Flyweight Pattern) 来提高性能和资源利用效率。
在 Java 虚拟机 (JVM) 中的类加载器 (ClassLoader) 实现中通常使用了以下两种设计模式
委托模式 (Delegation Pattern)ClassLoader 的实现通常使用了委托模式来处理类加载请求。委托模式指的是当一个对象收到请求时它将请求委托给其他对象来处理。在类加载器的情况下当一个类加载器接收到加载类的请求时它首先会将请求委托给父类加载器进行处理。如果父类加载器无法加载该类子类加载器才会尝试加载类。这种委托模式的设计方式可以实现类加载器的层次结构从而实现类加载器的隔离和类加载的委派。单一职责模式 (Single Responsibility Pattern)ClassLoader 的主要责任是加载类文件并定义对应的类。ClassLoader 的设计符合单一职责模式即一个类应该只负责一项职责。ClassLoader 将类加载的职责封装在一个独立的类中从而使得类加载的逻辑与其他功能解耦提高了代码的可维护性和可扩展性。
除了委托模式和单一职责模式ClassLoader 的实现可能还涉及其他设计模式具体取决于实际的实现细节和需求。例如一些类加载器的缓存机制可能使用了享元模式 (Flyweight Pattern) 来提高性能和资源利用效率。