当前位置: 首页 > news >正文

JVM面试题——类加载器

目录

类加载器

类加载器的分类

类的加载过程*

类加载核心机制*

类加载器的作用*

什么是双亲委派模型?***

怎么打破双亲委派模型


类加载器

类加载器的分类

类加载器之间是父子层级关系(自下而上委托)

1. 引导类加载器(BootstrapClassLoader / 启动类加载器)

  • 实现语言:C++(JVM 内核实现,无 Java 类对应)

  • 加载范围:JRElib目录下的核心类库(如rt.jarjava.lang.*等 JDK 核心类)

  • 作用:支撑 JVM 自身运行,是最顶层的类加载器

2. 扩展类加载器(ExtClassLoader / PlatformClassLoader,Java 9+ 更名为平台类加载器)

  • 实现语言:Java,派生(继承)自ClassLoader

  • 加载范围:JRElib/ext目录下的扩展 JAR 包(Java 9+ 改为加载平台模块)

  • 作用:加载 JDK 扩展功能类

3. 应用程序类加载器(AppClassLoader / 系统类加载器)

  • 实现语言:Java,派生自ClassLoader

  • 加载范围classpath路径下的类包(即我们自己写的 Java 类、第三方依赖包)

  • 作用:默认加载用户程序的类,是程序中最常用的类加载器

4. 自定义类加载器

  • 实现方式:程序员继承ClassLoader类,重写loadClass()findClass()方法

  • 加载范围:用户自定义路径下的类包(如网络、加密文件等特殊来源)

  • 作用:实现热部署、模块化加载、类隔离等高级功能

类的加载过程*

核心定义

类加载是 JVM 将.class文件读入内存,并最终生成可用Class对象的过程。核心分为 3 大阶段:加载 → 链接 → 初始化,再加上「使用」和「卸载」,共同构成类的完整生命周期。

完整流

1. 加载(Loading)

  • 核心动作:通过类加载器,将.class文件读入 JVM 内存。

  • 具体步骤:

    1. 根据类的全限定名(如com.example.User),获取对应的.class文件(来源:本地文件、Jar 包、网络等)。

    2. .class文件的静态存储结构,转换为方法区的运行时数据结构。

    3. 在堆内存中生成一个java.lang.Class对象,作为访问方法区类数据的入口(是 Java 反射的基础)。

  • 关键结论:类加载为懒加载,仅在使用时才加载,以节省内存。

2. 链接(Linking)

链接细分为 3 个阶段:验证 → 准备 → 解析

(1)验证(Verification)

  • 目的:确保.class文件符合 JVM 规范,无安全问题,不会危害虚拟机。

  • 检查内容:文件格式规范、版本兼容性、字节码合法性等。

(2)准备(Preparation)

  • 静态变量(static 修饰)在方法区分配内存,并设置类型默认初始值(如int为 0,booleanfalse,引用类型为null)。

  • 注意点:

    • 实例变量:在创建对象时分配到堆内存,不参与此阶段。

    • final static常量:编译期已确定值,准备阶段直接赋值,无默认值步骤。

(3)解析(Resolution)

  • 将常量池中的符号引用替换为直接引用

    • 符号引用:以字面量符号描述目标(如类名、方法名),不要求目标已加载。

    • 直接引用:指向内存中目标的指针或偏移量,要求目标已存在。

  1. 初始化(Initialization)

  • 核心:执行类构造器<clinit>()方法。

  • <clinit>()由编译器自动生成,收集:

    1. 所有静态变量的显式赋值动作

    2. 所有static{}静态代码块

  • 执行规则:

    • 若父类未初始化,先触发父类初始化。

    • 多线程环境下,JVM 会对()加锁,保证线程安全。

  • 最终效果:静态变量被赋值为代码指定值,静态代码块执行。

  1. 使用 & 卸载(不属于类加载过程,属于类生命周期)

  • 使用:类被实例化、调用方法、反射访问等。

  • 卸载:当类的Class对象不再被引用,且类加载器也被回收时,JVM 卸载该类,释放方法区内存。

类加载核心机制*

虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、解析和初始化,最终形成可以被虚拟机直接使用的 java 类型。

1. 懒加载(动态加载)

  • 不会一次性加载所有类,仅在需要时才加载(如new对象、调用静态方法、反射),节省内存。

  • 基础类(如Object)和父类会优先加载。

2. 加载方式

  • 隐式加载:程序运行中遇到new、调用静态方法等,自动触发类加载。

  • 显式加载:手动调用Class.forName()ClassLoader.loadClass()等方法加载类。

3. 类加载器

  • 由 JVM 提供(系统类加载器),也可自定义(继承ClassLoader)。

  • 作用:从不同来源读取.class文件,最终产物是堆中的Class对象。

类加载器的作用*

核心定义

类加载器(ClassLoader)是 JVM 中负责搬运.class字节码文件的组件,就像 “快递员” 一样,把编译后的 Class 文件从外部(硬盘、Jar 包、网络等)运输到 JVM 内存中。

核心作用

  1. 加载.class文件

    • 根据类的全限定名,读取对应的.class文件(字节码文件)。

    • 校验.class文件的合法性:文件开头必须有固定标识CA FE BA BE(咖啡宝贝),不符合格式的文件会被拒绝加载。

  2. 边界清晰:只负责加载,不负责执行

    • ClassLoader 只完成 “把类读进内存” 这一步,不决定类是否可以运行

    • 类的字节码能否执行、如何执行,由 JVM 的执行引擎(Execution Engine负责。

  3. 生成访问入口

    • 加载完成后,在堆内存中生成对应的Class对象,作为程序访问方法区类数据的入口(也是 Java 反射的基础)。

什么是双亲委派模型?***

核心定义

双亲委派模型是 Java 类加载器的安全加载机制(也叫沙箱安全机制),核心规则是:

当一个类加载器收到加载请求时,不会自己先加载,而是把请求向上委托给父类加载器,直到顶层引导类加载器;

若父类加载器无法加载,再由子类加载器尝试加载。

完整流程(两步走)

第一步:自下而上(委托)

  1. 自定义类加载器 → 委托给 应用程序类加载器(AppClassLoader)

  2. 应用程序类加载器 → 委托给 平台类加载器(PlatformClassLoader)

  3. 平台类加载器 → 委托给 引导类加载器(BootstrapClassLoader,最顶层)

第二步:自上而下(尝试加载)

  1. 引导类加载器先尝试加载,若失败 → 交给平台类加载器

  2. 平台类加载器尝试加载,若失败 → 交给应用程序类加载器

  3. 应用程序类加载器尝试加载,若失败 → 抛出ClassNotFoundException

核心目的

  1. 安全性(最重要):防止核心类被篡改或恶意替代

    • 核心原理:JVM 规定,java.lang等核心包下的类,只能由最顶层的引导类加载器(BootstrapClassLoader)加载

    • 举例说明:当你尝试自定义java.lang.String类时,无论你的代码是否实际使用原生 String,这个加载请求都会沿着层级一路委托到 BootstrapClassLoader。

    • 最终结果:BootstrapClassLoader 发现java.lang.String是自己的核心职责,会直接加载官方 JDK 中的原生 String 类,直接拦截并阻止下层加载器继续处理。因此,你的自定义 String 类不仅不会被加载,甚至连 “被尝试加载” 的机会都没有,从根源上杜绝了核心类被覆盖的风险。

  2. 性能:避免重复加载

    • 下层加载器发现类已被上层加载,就不再重复加载,提升效率。

怎么打破双亲委派模型

核心操作

  • 自定义类加载器,继承ClassLoader

  • 重写两个关键方法:

    1. loadClass():破坏 “向上委托” 的逻辑

    2. findClass():实现自定义的类加载逻辑

典型场景

  • Tomcat 自定义类加载器、热部署、模块化加载等场景会打破该模型。

http://www.jsqmd.com/news/561950/

相关文章:

  • 2026年正德书院官方联系方式公示,中考复读与冲刺教育合作便捷入口 - 第三方测评
  • 对于对话中的对话状态跟踪,OpenClaw 的跨领域迁移能力?
  • 状态量: 轮速、滑移率、附着系数
  • Spring Data MongoDB 实战指南:从基础映射到高效CRUD与避坑技巧
  • Kubernetes集群资源优化架构:基于Descheduler的智能再调度系统设计
  • Windows 10 USB断连:精准排查电源与驱动问题
  • java工具:《Java 8 Stream实战:一行代码搞定集合对象类型转换》
  • 激光雷达点云处理—从原理到实战工具链
  • 手把手教你用MestRenova和Chemdraw解析核磁共振谱图(附实战案例)
  • OpenClaw 的模型服务是否支持基于策略的流量控制?
  • WindowsCleaner深度解析:如何用开源工具轻松解决C盘空间不足问题
  • [EAI-034] 迭代式强化学习优化VLA模型的稳定性与泛化能力
  • Windows系统DLL文件缺失?手把手教你修复appvisvsubsystems64_arm64x.dll等常见错误
  • 用好 Claude Code 的那些门道
  • 避坑指南:Libero仿真波形怎么看?综合前、综合后、布局布线后三次仿真的区别与意义
  • DeepSeek-R1-Distill-Qwen-7B推理优化实战:5步完成Ollama本地部署
  • 2048 AI辅助工具深度剖析:从算法核心到跨平台实践
  • 银河麒麟V4.0.2-sp4系统初始化实战:网络、DNS与软件源一站式配置指南
  • 音乐界面定制:foobar2000皮肤的个性化体验方案
  • Unity场景的面数上限
  • Qwen3.5-4B-Claude-Opus效果展示:编程语言特性对比(如Python/Go)
  • 基于51单片机的甲醛监测以及通风控制系统(有完整资料)
  • 缝纫机SW三维模型
  • 基于Jenkins的前后端分离项目自动化部署实战指南
  • 自动送料装车系统PLC控制的设计——24页
  • 终极抢票神器:Python自动化脚本让你轻松锁定心仪演出门票
  • 【LeetCode】118.杨辉三角
  • ncmdumpGUI:如何突破网易云音乐加密限制实现跨设备自由播放
  • 3大核心功能解锁虚幻引擎游戏深度定制:UE4SS实战指南
  • STM32H750VBT6网络实战:CubeIDE+FreeRTOS+LWIP保姆级配置,从零搞定LAN8720A以太网通信