什么是JVM——餐厅类比
目录一、核心前提二、JVM 整体定位餐厅类比总纲三、JVM 核心模块拆解餐厅类比 1:1 对应模块 1类加载器子系统 → 餐厅 “收单 归档员”核心动作关键补充对应你的内存疑问模块 2运行时数据区 → 餐厅的 “场地划分”JVM 内存分区模块 3执行引擎 → 餐厅 “领班”核心解决你 “谁执行” 的困惑核心角色核心动作以 new User() 为例关键结论模块 4本地接口 → 餐厅 “外援联络员”核心动作四、完整运行流程餐厅版串所有模块步骤 1编译非餐厅做但必须有步骤 2类加载收单 归档员步骤 3准备物料领班 场地划分步骤 4翻译 执行领班 厨师步骤 5收尾领班 清洁工五、核心逻辑串记餐厅类比一、核心前提✅ JVM 运行在操作系统之上所有核心模块堆、方法区、栈、程序计数器都是 JVM 在操作系统分配给它的内存空间里自己划分的 “虚拟区域”✅ 操作系统本身没有 “堆、方法区、栈” 这些概念是 JVM 为了管理方便把自己占用的内存拆分成了这些功能区✅.class文件加载的本质把硬盘上的.class数据读入内存解析后存到 JVM 自己划分的 “方法区” 里✅ JVM 的程序计数器 ≠ CPU 的程序计数器JVM 程序计数器是 JVM 在内存里给自己的线程设的 “进度条”只记字节码执行位置CPU 程序计数器是 CPU 硬件层面的寄存器记机器码执行位置关系JVM 程序计数器的指令地址最终会被执行引擎翻译成 CPU 程序计数器能识别的机器码地址。为什么 JVM 要自己划分内存区域分工明确堆存 “实物”、栈存 “临时数据”、方法区存 “说明书”避免混乱效率更高栈的临时数据执行完就释放不用等清洁工GC堆的实物统一回收减少内存浪费跨平台屏蔽不同操作系统的内存管理差异保证 Java 程序 “一次编译到处运行”。二、JVM 整体定位餐厅类比总纲把 JVM 比作一家能独立完成 “从接单到出餐” 的餐厅餐厅JVM运行在商场操作系统里商场给餐厅分配了一块固定的场地操作系统给 JVM 分配的内存餐厅自己把场地划分成 “后厨、仓库、工位、档案柜”JVM 把内存划分成堆、方法区、栈等顾客的点餐需求你的 Java 代码→ 编译成餐厅能懂的 “标准化菜谱”.class 字节码→ 餐厅全程负责 “读菜谱→备食材→做菜→收盘子”。三、JVM 核心模块拆解餐厅类比 1:1 对应模块 1类加载器子系统 → 餐厅 “收单 归档员”核心动作从硬盘餐厅的外卖平台读取 “标准化菜谱”.class 文件校验菜谱合法性比如是不是正版、有没有篡改—— 对应 “双亲委派模型”把菜谱的 “电子版”类的元数据类名、方法、变量、常量存到餐厅的 “档案柜”方法区注意只存 “菜谱电子版”不存食材也不做菜。关键补充对应你的内存疑问档案柜方法区是餐厅JVM在自己的场地内存里划分的一块区域不是商场操作系统自带的加载.class 把硬盘上的菜谱内容读进内存存到档案柜里方便后续随时查阅。模块 2运行时数据区 → 餐厅的 “场地划分”JVM 内存分区JVM 内存区域餐厅对应区域给谁用存什么通俗版核心细节解决你的疑问堆食材仓库全餐厅共用存放 “实物食材”所有new出来的对象比如 User 对象1. 仓库是餐厅自己划分的不是商场的公共仓库2. 食材没人要了清洁工GC会来清理3. 做菜前必须先在仓库占位置内存分配。方法区元空间档案柜全餐厅共用存放 “菜谱电子版”类的元数据、常量、静态变量1. JDK8 前档案柜在食材仓库里隔了一小块永久代2. JDK8 后档案柜改用商场的备用空间操作系统本地内存元空间不容易满3. 只存菜谱不存食材。虚拟机栈厨师专属工位单个厨师线程专用每个菜谱步骤方法对应一个 “工位托盘栈帧”存当前步骤要用的 “临时配料、工具”方法参数、局部变量1. 厨师开始做菜方法调用 新增托盘压栈做完方法结束 撤托盘出栈2. 托盘只放 “食材标签对象地址”不放食材本身3. 厨师同时做太多步骤递归过深→ 托盘摆不下栈溢出 StackOverflowError4. 工位是餐厅划分的每个厨师独立互不干扰。本地方法栈外援专用工位单个厨师线程专用给调用 “外援团队”操作系统本地库的步骤native 方法准备的工位比如餐厅做不了海鲜加工找商场的海鲜团队操作系统帮忙这个工位就是给对接外援用的。程序计数器厨师的进度条贴纸单个厨师线程专用贴在菜谱上记着 “下一个步骤该做哪一步”下一条字节码的地址1. 是餐厅自己给厨师贴的贴纸JVM 自己设的内存区域不是商场 / CPU 自带的2. 厨师临时离开线程切换回来能按贴纸继续做3. 贴纸永远够贴不会 OOM4. 和 CPU 的进度条CPU 程序计数器的区别贴纸记 “菜谱步骤”CPU 进度条记 “做菜动作”。注栈帧是 “完整的做菜托盘”包含 4 个功能区对象地址只是「局部变量表」里的一种内容基本数据类型直接存在栈帧里不用指向其他内存只有对象 / 方法引用需要通过 “地址” 指向堆 / 方法区栈帧本身是独立的内存区域包含计算、调用、收尾的所有上下文。栈帧厨师托盘的精准类比把「栈帧 厨师的专属做菜托盘」托盘里明确划分 4 个区域对应栈帧的 4 个核心部分栈帧核心部分餐厅托盘对应区域存什么精准 通俗关键细节对应技术特性1. 局部变量表托盘左侧「配料格」① 基础配料8 种基本类型比如 int6 个鸡蛋、double3.5 勺盐② 食材标签对象引用比如 “User 对象在仓库 A 区 10 号货架”③ 回位贴returnAddress做完这步要回到菜谱的第 20 行① 配料格的数量在拿到菜谱时就定死了编译期确定大小做菜时不能加 / 减② 只放 “现成配料” 或 “食材标签”不放食材本身对象在堆里③ 每个厨师的托盘独立配料格不会混线程安全。2. 操作数栈托盘中间「操作台」临时放做菜的中间产物比如做i6*6先放 6 个鸡蛋、再放 6 个鸡蛋到操作台→翻炒计算→得到 12 个鸡蛋→放进左侧配料格局部变量表① 操作台是 “临时计算区”做完一步就清空② 只能按 “先放后拿” 的规则用栈式操作比如先放的 6 个鸡蛋要后拿。3. 动态链接托盘右侧「菜谱指引单」写着 “要调用的步骤在哪”比如做 “番茄炒蛋” 要调用 “切番茄” 步骤指引单写着 “切番茄的菜谱在档案柜第 5 层第 3 本”符号引用→实际地址① 指引单在做菜时才会精准指向档案柜的具体菜谱运行时解析② 避免拿错菜谱保证方法调用的准确性。4. 方法出口托盘底部「收尾贴」① 正常贴做完这步回到菜谱第 20 行return 后执行的地址② 异常贴炒糊了要去 “处理糊菜” 的步骤异常处理地址① 保证做菜流程不中断要么正常收尾要么异常兜底② 对应方法结束后 “出栈” 的逻辑做完菜按收尾贴回到上一步再撤掉当前托盘。模块 3执行引擎 → 餐厅 “领班”核心解决你 “谁执行” 的困惑核心角色绝不自己做菜只做 “解读菜谱 安排物料 翻译菜谱 找厨师干活 安排清洁工”子角色分工解释器刚开业时逐行读菜谱翻译给厨师CPU保证快速出第一道菜程序快速启动JIT 编译器同一道菜做 100 次热点代码把菜谱翻译成 “速记版”优化后的机器码并保存下次直接用执行更快GC清洁工定期去食材仓库堆清没人要的食材无引用对象。核心动作以new User()为例看厨师的进度条贴纸程序计数器“该做 User 这道菜了”安排仓库管理员“在食材仓库堆给 User 食材留个位置”内存分配安排厨师工位“给当前厨师加个托盘栈帧贴个 User 食材的标签对象地址”把菜谱步骤字节码翻译成厨师能懂的 “做菜指令”机器码把指令交给厨师CPU让厨师真正动手做菜厨师做完后更新进度条贴纸程序计数器“下一个步骤该做什么”。关键结论领班执行引擎 调度 翻译厨师CPU 唯一执行者所有调度动作分配内存、创建栈帧都在餐厅JVM自己的场地内存里完成和商场操作系统无关。模块 4本地接口 → 餐厅 “外援联络员”核心动作餐厅JVM自己干不了的活比如调用操作系统的文件读写、硬件操作通过联络员本地接口找商场的外援团队C/C 本地库帮忙对接外援的专用工位本地方法栈。四、完整运行流程餐厅版串所有模块步骤 1编译非餐厅做但必须有你写的 “手写菜谱”User.java→ 用 “菜谱转换器”javac→ 转成餐厅能懂的 “标准化菜谱”User.class。步骤 2类加载收单 归档员收单员读标准化菜谱User.class校验合法性把菜谱电子版存到档案柜方法区。步骤 3准备物料领班 场地划分领班看厨师的进度条贴纸“该做 User 这道菜了”领班通知仓库“给 User 食材留位置”堆内存分配领班通知厨师工位“加个托盘贴 User 食材标签”栈帧创建。步骤 4翻译 执行领班 厨师开业初期领班逐行翻译 User 菜谱给厨师解释器User 菜做 100 次领班把菜谱改成速记版并保存JIT 编译缓存领班把做菜指令交给厨师CPU厨师按指令在仓库拿食材做完 User 这道菜CPU 执行机器码初始化对象。步骤 5收尾领班 清洁工进度条贴纸更新“下一道菜该做什么”若 User 食材没人用了清洁工GC清理仓库里的 User 食材。五、核心逻辑串记JVM 是操作系统上的 “虚拟餐厅”所有内存分区都是餐厅自己划分的和操作系统无关类加载器 收单归档运行时数据区 餐厅场地执行引擎 领班调度 翻译CPU 厨师唯一执行者程序计数器是 JVM 给线程贴的 “进度条”和 CPU 的硬件计数器不是一回事所有动作的核心JVM 统筹内存和指令CPU 只执行翻译后的机器码。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2459943.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!