类加载机制(一)

el/2024/2/25 22:34:16

类加载

定义:

​ 当 用到某个类 时,首先需要通过 类加载器 把类加载到JVM上

组成:

  • 引导类加载器 —— 加载lib目录
  • 扩展类加载器 —— 加载ext目录
  • 应用程序类加载器 —— 加载ClassPath目录 【用户写的】
  • 自定义加载器 —— 加载用户自定义目录

※ 类运行加载全过程图

调用main()方法流程

类加载流程

定义:

​ JVM加载类的流程如下:

步骤作用
加载字节码load内存,生成 java.lang.Class对象
验证校验字节码文件的正确性
准备静态变量分配内存,赋默认值
解析符号引用 (英文等符号)替换为 直接引用 (内存地址) 【静态链接】
初始化静态变量 初始化指定值、执行 静态代码块

类加载流程

特点:

懒加载机制,用到再加载

类加载器

定义:

​ Java里有如下四种类加载器,除了引导类加载器,其他类加载器的父类均为ClassLoader

  • 引导类加载器
  • 扩展类加载器
  • 应用程序类加载器
  • 自定义加载器

引导类加载器

定义:

Bootstrap类加载器,用于加载java核心类库

初始化流程:

​ 通过C++创建一个引导类加载器 实例

Launcher类

定义:

​ 它由引导类加载器加载,然后加载了扩展类加载器和应用类加载器

​ Launcher类的getClassLoader()方法,返回是==应用类加载器==

扩展类加载器

定义:

Ext类加载器,用于加载java的一些扩展类库

初始化流程:

  • 引导类加载器 加载了Launcher类实例

  • Launcher类的构造方法中创建了扩展类加载器

源码:

//Launcher的构造方法
public Launcher() {Launcher.ExtClassLoader var1;try {//构造扩展类加载器,在构造的过程中将其父加载器设置为nullvar1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);}
}// 通过双重检测锁来构造扩展类加载器 DCL
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {// DCLif (instance == null) {Class var0 = Launcher.ExtClassLoader.class;synchronized(Launcher.ExtClassLoader.class) {if (instance == null) {instance = createExtClassLoader();}}}return instance;
}

应用类加载器

定义:

应用类加载器,用于加载我们的应用程序

初始化流程:

  • 引导类加载器 加载了Launcher类实例

  • Launcher类的构造方法中创建了应用类加载器,并将父加载器不是父类】指定为 引导类加载器用于双亲委派机制

//Launcher的构造方法
public Launcher() {Launcher.ExtClassLoader var1;//构造扩展类加载器,在构造的过程中将其父加载器设置为nullvar1 = Launcher.ExtClassLoader.getExtClassLoader();try {//构造应用类加载器,在构造的过程中将其父加载器设置为ExtClassLoader 【var1】//getClassLoader() 获得应用类加载器this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);}
}

双亲委派机制

定义:

​ JVM类加载器在加载类时,是根据双亲委派机制来进行加载的

​ 简单说,子加载器不先加载类,委托父加载器进行加载,如果父加载器加载不了该类,才让子加载器加载。

源码:

//ClassLoader的loadClass方法,里面实现了双亲委派机制
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(c == null)if (parent != null) {     // 如果当前加载器父加载器不为空,则委托父加载器加载该类c = parent.loadClass(name, false);} else {// 如果当前加载器的父加载器为空,则委托引导类加载器加载该类c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// 找不到该类}if (c == null) {long t1 = System.nanoTime();//都会调用URLClassLoader的findClass()方法在加载器的类路径里查找并加载该类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;}
}

小结:

双亲委派机制ClassLoader.loadClass()方法中已经定义好,通过之前Launcher类设置的parent的关系,让父加载器先加载;Ext扩展类加载器没有parent,通过方法 findBootstrapClassOrNull() 委托引导类加载器加载

双亲委派机制图解

为什么要设计双亲委派机制?

  • 沙箱安全机制 —— 与 rt.jar 等同包同名的类,不会被加载,防止核心API类库被修改
  • 避免类的重复加载 —— 父加载器已经加载过了的类,没必要让子类重复加载一次,确保 被加载类的唯一性

全盘负责委托机制

定义:

全盘负责是指:一个类被一个ClassLoader加载过后,该类所依赖及引用的类都由这个ClassLoader加载 【除非显式指定ClassLoader】

自定义类加载器

定义:

​ Java支持自定义类加载器,条件有二:

  • 继承 java.lang.ClassLoader
  • 重写findClass()方法

例子:

public class MyClassLoaderTest {static class MyClassLoader extends ClassLoader {private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}private byte[] loadByte(String name) throws Exception {name = name.replaceAll("\\.", "/");FileInputStream fis = new FileInputStream(classPath + "/" + name+ ".class");int len = fis.available();byte[] data = new byte[len];fis.read(data);fis.close();return data;}// 重要方法protected Class<?> findClass(String name) throws ClassNotFoundException {try {// 将.class类文件加载到内存byte[] data = loadByte(name);//defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}}public static void main(String args[]) throws Exception {//初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader// MyClassLoader的父类是AppClassLoaderMyClassLoader classLoader = new MyClassLoader("D:/test");//D盘创建 test/com/summer/jvm 几级目录,将User类的复制类User1.class丢入该目录Class clazz = classLoader.loadClass("com.summer.jvm.User1");Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}
}

注意:

​ 如果父加载器有com.summer.jvm.User1类,那么不会用自定义类加载器

​ 此时需要重写loadClass()方法,修改双亲委派机制的逻辑,让自定义类不要往上委派


http://www.ngui.cc/el/4893704.html

相关文章

JVM内存模型(二)

JVM内存模型 定义&#xff1a; ​ JVM内存模型&#xff0c;又称为运行时数据区。【区别Java内存模型】 紫色 —— 线程私有 浅黄色 —— 线程共享 虚拟机栈 定义&#xff1a; ​ 虚拟机栈是一个线程私有的、FILO的结构。 ​ 一个线程到来时&#xff0c;从虚拟机栈中&…

JVM内存分配机制详解(三)

JVM对象创建过程 1.类加载检查 流程&#xff1a; JVM在遇到一条new指令检查这个指令的参数是否在常量池中定位到一个类的符号引用 【方法区】并检查 符号引用 所代表的类是否被类加载器加载 包括 new关键字、对象克隆、对象序列化等等 **总的来说&#xff1a;**执行一个类…

Spring IOC (三)

Spring IOC &#xff08;三&#xff09; refresh() 后八个方法 refresh() 定义&#xff1a; ​ 这个方法中&#xff0c;内置了13个方法&#xff0c;做了很多事情 方法&#xff1a; 方法作用prepareRefresh()保存了容器的启动时间&#xff0c;启动标志等obtainFreshBeanFac…

Spring循环依赖(四)

循环依赖 什么是循环依赖&#xff1f; 依赖&#xff1a;在Spring中&#xff0c;A类将其他的类当作属性时&#xff0c;就造成了A依赖其他类 定义&#xff1a; ​ 现有A、B两个类&#xff0c;如果A类将B类作为属性&#xff08;并设置Autowired&#xff09;&#xff0c;B类也将…

想要学习Python?8年老Python程序员的自学书单曝光,入门必看

什么是Python&#xff1f; 它是一种类似C语言的人工智能便捷语言。它简单易学、并且开源免费&#xff0c;应用面广&#xff0c;所以&#xff0c;是自学门槛相对低的一种计算机语言&#xff0c;能为你的生活、工作提升效率。 为什么选择学习python的人越来越多&#xff1f; 全…

Python基础学习:都给我学起来,这样快速处理CSV、JSON和XML数据的方法太简便了

Python的卓越灵活性和易用性使其成为最受欢迎的编程语言之一&#xff0c;尤其是对于数据处理和机器学习方面来说&#xff0c;其强大的数据处理库和算法库使得python成为入门数据科学的首选语言。在日常使用中&#xff0c;CSV&#xff0c;JSON和XML三种数据格式占据主导地位。下…

Python 强大的信号库:必看的blinker 入门级教程,简单易懂

1 信号 信号是一种通知或者说通信的方式&#xff0c;信号分为发送方和接收方。发送方发送一种信号&#xff0c;接收方收到信号的进程会跳入信号处理函数&#xff0c;执行完后再跳回原来的位置继续执行。 常见的 Linux 中的信号&#xff0c;通过键盘输入 CtrlC&#xff0c;就是…

初学者别找了,Python Pandas快速入门教程,吐血总结全网最详细教程

Python 是开源的&#xff0c;它很棒&#xff0c;但是也无法避免开源的一些固有问题&#xff1a;很多包都在做&#xff08;或者在尝试做&#xff09;同样的事情。如果你是 Python 新手&#xff0c;那么你很难知道某个特定任务的最佳包是哪个&#xff0c;你需要有经验的人告诉你。…

吐血整理:大厂Python学习路线,从青铜到王者,从此打卡学习不迷茫

前言 小编知道 很多朋友都对成为技术大牛有着深深的向往 并有很多技术问题想要解决 因为市面上各种教程质量良莠不齐 而且想要掌握高阶的开发技术 需要耗费大量的时间和精力 现如今每天有数以百万计的人使用 Python &#xff0c;用户群呈现出指数级增长&#xff0c;几乎没…

【避坑指南】Python12个坑爹的陷阱,再不看坑的就是你

我个人对陷阱的定义是这样的&#xff1a;代码看起来可以工作&#xff0c;但不是以你“想当然”的方式。如果一段代码直接出错&#xff0c;抛出了异常&#xff0c;我不认为这是陷阱。比如&#xff0c;Python程序员应该都遇到过的“UnboundLocalError”, 示例&#xff1a; >&…