JVM——八股文

1. JDK, JRE和JVM的关系

  • JDK = JRE + Java开发工具
  • JRE = JVM + Java核心类库

JDK供Java程序开发人员开发软件,JRE供客户使用,只需要JVM运行环境即可。

JVM运行的是class字节码,不仅能运行Java代码,还能运行其他语言,只要语言能编译成字节码文件即可,比如Kotlin。

2. JVM的主要组成部分

  • 类加载器
    • 加载Loading
    • 链接Linking
    • 初始化Initialization
  • 运行时数据区
  • 执行引擎
  • 本地接口

3. 你能解释一下JVM类加载器的作用吗

1. 加载 (Loading)

  • 目的:将类的 .class 文件的二进制数据读入内存,并在方法区中创建一个 java.lang.Class 对象来表示这个类。
  • 主要工作
    1. 通过类的全限定名获取其二进制字节流:这可以通过多种方式实现,比如从本地 .class 文件、JAR 包、网络、动态生成(如 Proxy 类)、数据库等。
    2. 将字节流代表的静态存储结构转化为方法区的运行时数据结构:JVM 解析字节码,并在方法区(Method Area)或元空间(Metaspace,Java 8+)中创建该类的数据结构。
    3. 在内存中创建一个 java.lang.Class 对象:这个对象是 java.lang.Class 的实例,它作为程序访问该类各种数据的入口。这个对象通常存储在堆(Heap)中。
  • 关键点
    • 这个阶段主要由类加载器(ClassLoader) 完成。
    • 类加载器遵循双亲委派模型(Parent Delegation Model),即先委托父类加载器尝试加载,只有当父类加载器无法完成时,子加载器才会尝试自己加载。

2. 链接 (Linking)

链接阶段确保加载的类是正确且符合 JVM 规范的,并为其分配内存。它分为三个子阶段:

(1) 验证 (Verification)

  • 目的:确保 .class 文件的字节流包含的信息符合当前 JVM 的要求,不会危害 JVM 的安全。
  • 主要检查
    • 文件格式验证:检查字节流是否符合 .class 文件格式规范(如魔数 0xCAFEBABE、版本号等)。
    • 元数据验证:检查类的元数据信息是否有矛盾(如是否继承了 final 类、是否实现了不存在的接口等)。
    • 字节码验证:这是最复杂和关键的一步。通过数据流和控制流分析,确定字节码指令不会做出危害 JVM 安全的操作(如类型转换错误、非法跳转、访问不存在的字段等)。
    • 符号引用验证:确保解析动作能正常执行(如检查符号引用中描述的类、字段、方法是否存在)。
  • 重要性:这是 JVM 防止恶意代码攻击的重要屏障。虽然验证很耗时,但可以保证运行时的安全性。

(2) 准备 (Preparation)

  • 目的:为类的静态变量static fields)分配内存,并设置这些变量的初始值
  • 关键点
    • 分配内存:在方法区(或元空间)为 static 变量分配内存。
    • 设置初始值:这里的“初始值”通常是零值(zero value),而不是你在代码中赋的值。
      • int 类型的 static 变量初始值为 0
      • boolean 类型的 static 变量初始值为 false
      • 引用类型(Object)的 static 变量初始值为 null
    • final static 常量:如果 static 变量同时被 final 修饰,并且是基本类型或 String 字面量,那么它的值(编译期常量)会在这个阶段直接赋值,而不是零值。例如:public static final int MAX = 100; 的值 100 会在准备阶段就设置好。

(3) 解析 (Resolution)

  • 目的:将常量池内的符号引用(Symbolic References)替换为直接引用(Direct References)。
  • 概念解释
    • 符号引用:以一组符号来描述所引用的目标。它可以是任何形式的字面量,只要能无歧义地定位到目标即可。例如,常量池中用 类名.方法名.描述符 来表示一个方法。
    • 直接引用:可以直接指向目标的指针、相对偏移量或一个能间接定位到目标的句柄。直接引用是与内存布局相关的。
  • 解析的内容
    • 类或接口解析:将符号引用解析为具体的类或接口的 Class 对象。
    • 字段解析:将符号引用解析为字段在类中的内存偏移量。
    • 方法解析:将符号引用解析为方法在方法表中的索引或直接指针。
    • 接口方法解析:类似方法解析。
  • 时机:解析动作不一定在链接阶段一次性完成,它可能在初始化之后才进行(称为“延迟解析”或“惰性解析”)。只有当真正需要使用某个符号引用时,才会触发解析。

3. 初始化 (Initialization)

  • 目的:执行类的初始化代码,为类的静态变量赋予程序中指定的值,并执行 static 代码块。
  • 主要工作
    • 执行 <clinit>() 方法。<clinit> 是由编译器自动收集类中所有 static 变量的赋值语句和 static 代码块中的语句合并产生的类构造器方法。
    • 按照代码中出现的顺序执行这些初始化语句。
  • 关键点
    • 这是类加载过程的最后一步
    • <clinit>() 方法是线程安全的:JVM 会保证一个类的 <clinit>() 方法在多线程环境下只被执行一次。其他线程会阻塞,直到第一个线程完成初始化。
    • 触发时机:这是主动使用一个类的时刻。以下操作会触发类的初始化:
      1. 创建类的实例(new 关键字)。
      2. 访问类的静态变量(public static 除外,final static 编译期常量也不会触发)。
      3. 调用类的静态方法。
      4. 使用反射(Class.forName())。
      5. 初始化一个类的子类(会先触发父类的初始化)。
      6. 虚拟机启动时,包含 main() 方法的主类。
      7. MethodHandle 和 VarHandle 的某些操作。
    • 被动引用不会触发:访问 final static 编译期常量、通过子类引用父类的 static 变量(只会触发父类初始化,不会触发子类)、数组定义(new MyClass[10] 不会触发 MyClass 的初始化)等属于被动引用,不会触发初始化。

4. 使用 (Using)

  • 目的:类初始化完成后,就可以被程序正常使用了。
  • 工作:程序通过 new 创建对象、调用静态方法、访问实例方法等。

5. 卸载 (Unloading) 

  • 目的:当类不再被任何地方引用,满足垃圾回收条件时,JVM 可以卸载该类,回收其占用的内存(主要是方法区/元空间和 Class 对象本身)。
  • 条件:非常严格。需要该类的 ClassLoader 被回收、该类的所有实例都已被回收、该类的 Class 对象没有被任何地方引用。

总结流程图

加载 (Loading)↓
链接 (Linking)├── 验证 (Verification)├── 准备 (Preparation)  <-- static 变量赋零值 (或 final static 常量值)└── 解析 (Resolution)   <-- 符号引用 -> 直接引用↓
初始化 (Initialization)    <-- 执行 <clinit>(), static 变量赋程序值, 执行 static 块↓
使用 (Using)↓
卸载 (Unloading) (可选)

4. 你知道JVM的类加载器有哪些?双亲委派机制是什么?

一、JVM 的类加载器 (Class Loaders)

JVM 在启动时会创建一系列的类加载器,它们形成了一个层次结构。主要的类加载器有三种(从顶层到底层):

1. 启动类加载器 (Bootstrap ClassLoader)

  • 角色:最顶层的类加载器,是 JVM 自身的一部分,通常由 C/C++ 实现。
  • 负责加载
    • JAVA_HOME/lib 目录下的核心类库(如 rt.jartools.jarresources.jar 等)。
    • 或者被 -Xbootclasspath 参数指定的路径中的类库。
  • 特点
    • 用 C/C++ 编写,不是 Java 类,因此在 Java 代码中无法直接引用它(getClassLoader() 返回 null)。
    • 负责加载最基础、最核心的 Java 类(如 java.lang.*java.util.*java.io.* 等)。

2. 扩展类加载器 (Extension ClassLoader)

  • 角色Bootstrap ClassLoader 的子加载器,由 Java 实现。
  • 负责加载
    • JAVA_HOME/lib/ext 目录下的类库。
    • 或者被 java.ext.dirs 系统变量所指定的路径中的所有类库。
  • 特点
    • 允许开发者将具有通用功能的 JAR 包放在这个目录下,自动被加载,无需在 -classpath 中指定。
    • 在 Java 9 的模块化系统(JPMS)之后,其重要性有所下降。

3. 应用程序类加载器 (Application ClassLoader) / 系统类加载器 (System ClassLoader)

  • 角色Extension ClassLoader 的子加载器,也是 Java 实现。
  • 负责加载
    • 用户类路径(ClassPath)上所指定的类库。
    • 即我们通常通过 -classpath 或 -cp 参数指定的 .jar 文件或 .class 文件目录。
  • 特点
    • 这是默认的类加载器,我们编写的 Java 类和第三方依赖库(如 Maven/Gradle 依赖)通常由它加载。
    • 可以通过 ClassLoader.getSystemClassLoader() 获取它的实例。

(可选) 自定义类加载器 (Custom ClassLoader)

  • 角色:开发者可以继承 java.lang.ClassLoader 类来创建自己的类加载器。
  • 目的
    • 从非标准来源加载类(如网络、数据库、加密的 JAR 包)。
    • 实现类的隔离(如 Tomcat 的 Web 应用隔离、OSGi 模块化)。
    • 实现热部署(Hot Swap)。
  • 常用场景:Web 服务器(Tomcat, Jetty)、应用服务器(WebLogic, WebSphere)、插件化框架、热更新系统。

二、双亲委派机制 (Parent Delegation Model)

双亲委派机制是 JVM 类加载器加载类时遵循的一种工作模式。它的核心思想是:当一个类加载器收到类加载请求时,它不会自己先去加载,而是把这个请求委派给它的父类加载器去完成,每一层的类加载器都是如此,因此所有的加载请求最终都会传送到顶层的启动类加载器。只有当父类加载器无法完成这个加载请求(即在它的搜索路径下找不到所需的类)时,子加载器才会尝试自己去加载。

工作流程

  1. 发起请求:假设应用程序类加载器(AppClassLoader)收到一个加载 java.lang.String 的请求。
  2. 向上委派:AppClassLoader 不会直接加载,而是将请求委派给它的父加载器——扩展类加载器(ExtClassLoader)。
  3. 继续委派:ExtClassLoader 收到请求后,也不会直接加载,而是继续委派给它的父加载器——启动类加载器(Bootstrap ClassLoader)。
  4. 顶层尝试加载:Bootstrap ClassLoader 尝试在 rt.jar 等核心库中查找 java.lang.String,找到了,于是加载成功,返回 Class 对象。
  5. 逐层返回:加载结果从 Bootstrap ClassLoader 逐层返回给 ExtClassLoader,再返回给 AppClassLoader,最终返回给发起请求的代码。

如果父加载器找不到呢?

  • 假设请求加载一个用户自定义的类 com.example.MyClass
  • 请求最终传到 Bootstrap ClassLoader,它在核心库中找不到。
  • Bootstrap ClassLoader 返回失败。
  • 请求返回到 ExtClassLoader,它在 lib/ext 目录下也找不到。
  • 请求返回到 AppClassLoader,它在 ClassPath 下找到了 com.example.MyClass.class,于是由它自己加载。

为什么需要双亲委派机制?

  1. 避免类的重复加载

    • 保证一个类在 JVM 中只有一个唯一的 Class 对象。
    • 例如,无论哪个类加载器发起加载 java.lang.Object 的请求,最终都会由 Bootstrap ClassLoader 加载,确保所有地方使用的都是同一个 Object 类。
  2. 保证核心类库的安全性

    • 这是最关键的一点。它防止了恶意代码通过自定义类加载器来替换核心 Java 类。
    • 例如,你不能自己写一个 java.lang.String 类放在 ClassPath 下,期望它被加载。因为当请求到达时,Bootstrap ClassLoader 会先加载它自己的、可信的 String 类,你的恶意类永远没有机会被加载。
    • 这确保了 Java 核心 API 的稳定性和安全性

如何打破双亲委派?

虽然双亲委派是默认和推荐的模式,但在某些特殊场景下需要打破它:

  1. 基础类型回调用户代码

    • 典型例子JNDI (Java Naming and Directory Interface)。
    • JNDI 的核心类由 Bootstrap ClassLoader 加载,但它需要回调由应用程序提供的服务实现(SPI - Service Provider Interface)。
    • Bootstrap ClassLoader 无法加载应用类路径下的类。
    • 解决方案:通过线程上下文类加载器Thread.currentThread().getContextClassLoader())。这个加载器通常被设置为 AppClassLoader。JNDI 核心代码可以通过它来加载用户实现的 SPI 类,从而“逆向”委托。
  2. 实现热部署/模块化

    • 典型例子:Tomcat, OSGi。
    • 需要隔离不同 Web 应用或模块的类,避免相互影响和核心库冲突。
    • 解决方案:自定义类加载器,并重写 loadClass() 方法,改变委派逻辑。例如,Tomcat 的 Web 应用类加载器会优先尝试自己加载 Web 应用的类(/WEB-INF/classes/WEB-INF/lib),只有当自己找不到时,才委派给父加载器(打破了“先委派”的原则)。这实现了应用间的类隔离。

总结

  • 类加载器:Bootstrap -> Extension -> Application -> Custom,形成层次结构。
  • 双亲委派:加载请求优先向上委派给父加载器,父加载器无法完成时,子加载器才尝试自己加载。
  • 优点:保证类的唯一性、核心类库安全。
  • 打破场景:SPI(如 JNDI)、热部署/模块化(如 Tomcat, OSGi),通常通过线程上下文类加载器或重写 loadClass() 实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/97620.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/97620.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

骑行把带定期换,维乐 Skin Wrap 把带焕新骑行

在公路骑行的装备体系里&#xff0c;把带是最易被忽视却至关重要的“消耗品”。它是骑手手部与车身的直接连接&#xff0c;每一次转向、变速、刹车&#xff0c;都需通过把带传递力量与操控意图&#xff1b;同时&#xff0c;它还承担着吸汗、减震、保护车把的作用。可长期使用后…

LeetCode100-73矩阵置零

本文基于各个大佬的文章 上点关注下点赞&#xff0c;明天一定更灿烂&#xff01; 前言 Python基础好像会了又好像没会&#xff0c;所有我直接开始刷leetcode一边抄样例代码一边学习吧。本系列文章用来记录学习中的思考&#xff0c;写给自己看的&#xff0c;也欢迎大家在评论区指…

宁波市第八届网络安全大赛 -- Crypto -- WriteUp

宁波市第八届网络安全大赛 – Crypto – WriteUp Three-prime RSA task import gmpy2 from Crypto.Util.number import *from secret import flagp getPrime(512) q getPrime(512) r getPrime(512) n p * q * r random_num getPrime(28) D ((p q r) * random_num) % n …

大语言模型 (LLM) 与多模态大模型 (MLM)

文章目录概述&#xff1a;从“模型”到“大”模型1、大语言模型 (Large Language Model, LLM)1.1 定义与概述关键特征&#xff1a;1.2 核心技术与架构Transformer架构自注意力机制 (Self-Attention)1.3 训练过程1.4 工作原理2. 多模态大模型 (Multimodal Large Model, MLM)2.1 …

HTML应用指南:利用GET请求获取全国招商银行网点位置信息

招商银行&#xff08;China Merchants Bank, CMB&#xff09;作为中国领先的股份制商业银行&#xff0c;始终坚持“以客户为中心”的服务理念&#xff0c;致力于为个人客户、企业客户及机构客户提供专业、高效、便捷的综合金融服务。依托“轻型银行”战略与“金融科技银行”建设…

JVM性能监控工具的使用

了解JVM性能监控工具并能熟练使用&#xff0c;是Java开发者进阶的必备技能。下面本文将为你介绍一些主流的JVM性能监控工具及其使用方法&#xff0c;并通过一些场景案例来分析如何应用这些工具解决实际问题。 &#x1f6e0;️ JVM性能监控与调优工具指南 ✨ 工具概览 以下是几款…

【工作】一些找工作需要了解避雷的知识

面试前 1.公司的具体情况 公司全称&#xff0c;办公地点&#xff0c;涉及岗位 要求hr做个简单的公司介绍 2.岗位职责/业务方向 工作内容、公司业务 3.薪资待遇&#xff0c;构成&#xff0c;底薪&#xff0c;五险一金 问一下工资范围 底薪 &#xff08;有责&#xff0c;无…

五、练习2:Git分支操作

练习2&#xff1a;Git分支操作 练习目标 掌握Git分支的创建、切换、合并等操作&#xff0c;理解分支在开发中的作用。 练习步骤 步骤1&#xff1a;准备基础仓库 # 创建练习目录 mkdir branch-practice cd branch-practice# 初始化仓库 git init# 创建初始文件 echo "# 分支…

【笔记】算法设计:异或空间线性基

Content1.什么是异或&#xff08;定义和性质&#xff09;2.异或空间线性基的构造方法3.异或空间线性基的应用4.算法设计例举5.小结说明算法设计应用之前&#xff0c;首先明确异或空间线性基&#xff1a;一种数据结构。用于处理异或关系&#xff08;运算&#xff09;下的向量空间…

Filebeat采集数据与日志分析实战

&#x1f31f;Filebeat采集数据的原理 Filebeat默认按行采集数据&#xff0c;如果数据没有换行&#xff0c;则该条数据无法采集到 属于有状态服务&#xff0c;可以记录上一次采集数据的位置点信息 修改配置文件 vim /etc/filebeat/config/03-log-to-console.yaml filebeat.inp…

Fluent Bit针对kafka心跳重连机制详解(下)

#作者&#xff1a;程宏斌 文章目录disconnectreconnect接上篇&#xff1a;https://blog.csdn.net/qq_40477248/article/details/150957571?spm1001.2014.3001.5501disconnect 断开连接的情况主要是两种: 连接或传输过程中有错误发生 超时, 比如空闲时间超时 ** * Close and …

React 第七十一节 Router中generatePath的使用详解及注意事项

前言 generatePath 是 React Router 的一个实用工具函数&#xff0c;用于根据路径模式和参数对象生成实际的 URL 路径。它在需要动态构建链接的场景中非常有用&#xff0c;比如生成导航链接或重定向路径。 1、基本用法和注意事项 import { generatePath } from react-router-do…

Python 爬虫案例:爬取豆瓣电影 Top250 数据

一、案例背景与目标 豆瓣电影 Top250 是国内权威的电影评分榜单之一&#xff0c;包含电影名称、评分、评价人数、导演、主演、上映年份、国家 / 地区、类型等关键信息。本案例将使用 Python 编写爬虫&#xff0c;实现以下目标&#xff1a; 自动请求豆瓣电影 Top250 的 10 个分…

SPA安全警示:OAuth2.0致命漏洞

OAuth2.0在SPA应用中的安全陷阱SPA&#xff08;单页应用&#xff09;通常采用隐式授权&#xff08;Implicit Flow&#xff09;或PKCE&#xff08;Proof Key for Code Exchange&#xff09;授权模式&#xff0c;但存在以下安全隐患&#xff1a;隐式授权模式的漏洞访问令牌直接暴…

table表格字段明细展示

文章目录1、字段渲染2、异步请求展示明细3、hover展示问题3.1 基本逻辑3.2 hover时长判断3.3 renderhover表格字段明细展示&#xff0c;属于比较小的需求&#xff0c;但是也有一定交互细节&#xff0c;本文选取部分场景。 1、字段渲染 render和渲染组件是有区别的。render常见为…

主网上线后生态极速扩张的 Berachain 生态,有哪些值得关注的项目?

Berachain 是典型的将 DeFi 思维嵌入到共识机制中的 Layer1&#xff0c;其核心是 PoL&#xff08;Proof of Liquidity&#xff09;共识。PoL 要求验证者在获得区块奖励前&#xff0c;必须将流动性导入白名单协议&#xff0c;并由市场决定资金流向。这样&#xff0c;验证者的权重…

claude-code对比GitHub-Copilot

Claude Code 文档日期&#xff1a;2025 年 08 月 20 日 定位 项目级开发助手&#xff0c;专注于全局视野和复杂任务的处理。 特点 超长上下文支持&#xff1a;支持 200k 超长上下文&#xff0c;适合处理复杂项目。丰富的自定义命令&#xff1a;提供灵活的命令配置&#xff0c;满…

Roo Code自定义Mode(模式)

什么是自定义模式&#xff1f; 简单来说&#xff0c;自定义模式就像是给Roo Code穿上不同的"职业装"。你可以创建针对特定任务或工作流程量身定制的模式&#xff0c;让Roo在不同场景下表现出专业的行为。 这些模式分为两种类型&#xff1a;全局模式&#xff08;在所有…

Next.js渲染模式:SSR、SSG与ISR揭秘

Next.js 核心渲染模式深度解析&#xff1a;SSR、SSG 与 ISR 在构建现代 Web 应用时&#xff0c;性能和用户体验是至关重要的考量。Next.js 作为 React 生态中一个备受推崇的框架&#xff0c;其强大的服务端渲染&#xff08;SSR&#xff09;、静态站点生成&#xff08;SSG&#…

Veo Videos Generation API 对接说明

本文介绍了如何对接 Veo Videos Generation API&#xff0c;通过输入自定义参数生成Veo官方视频。 下面将详细阐述 Veo Videos Generation API 的对接流程。 申请流程 使用 API 前&#xff0c;需前往 Veo Videos Generation API 页面申请服务。进入页面后&#xff0c;点击「…