news 2026/11/2 1:48:35

第13章 JVM运行原理:回到程序幕后

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第13章 JVM运行原理:回到程序幕后

第13章 JVM运行原理:回到程序幕后

已经写过方法、对象、线程和反射,再看编译、类加载、内存区域与垃圾回收,就有自己的代码可以对照了。遇到 HotSpot 的具体实现时,要把它与所有 JVM 都应遵守的规则分开。

本章路线

  • 第1节 Java运行原理:程序在幕后忙什么

1. Java运行原理:程序在幕后忙什么

class 文件是谁加载的,方法调用放在哪里记录,不再使用的对象又由谁回收?接下来沿着这些问题看 JVM 的运行过程。

沿着编译、类加载、执行和回收讲一遍程序怎样运行,也要区分运行时内存结构与并发一章的 JMM。

1. 编译与运行

源码变成字节码,再被加载和执行。编译报错与运行时异常发生在不同阶段,排查时先确定自己走到了哪一步。

完整过程
┌──────────────────────────────────────────────────────────────────────┐ │ 编译阶段 运行阶段 │ │ │ │ Hello.java ──→ [javac] ──→ Hello.class ──→ [java] ──→ 控制台输出 │ │ (源码) (编译器) (字节码) (JVM) │ │ │ │ ┌────────┐ ┌────────┐ │ │ │ javac │ │ java │ │ │ └────────┘ └────────┘ │ │ 将 .java 编译成 .class 启动 JVM,加载并执行 .class │ │ 检查语法错误 解释执行 / JIT 即时编译为机器码 │ │ 内存管理 (GC) │ └──────────────────────────────────────────────────────────────────────┘
编译阶段详解
# javac 做了什么javac HelloWorld.java# 1. 词法分析:字符序列 → Token 流# 2. 语法分析:Token 流 → 抽象语法树 (AST)# 3. 语义分析:类型检查、变量检查等# 4. 生成字节码:AST → HelloWorld.class(.class 字节码文件)
# 反编译查看字节码javap-cHelloWorld.class
运行阶段详解
java HelloWorld 1. 类加载:ClassLoader 加载 HelloWorld.class 到 JVM 2. 字节码校验:验证 .class 文件格式正确性 3. 解释执行:JVM 逐条将字节码翻译为机器码执行 4. JIT 编译:热点代码(频繁执行)直接编译成本地机器码,提升性能 5. GC:自动回收不再使用的内存

2. JVM 运行时数据区域

这里讲运行时数据区域。栈帧、堆和方法区描述数据如何组织,不要把它们与并发一章的 Java 内存模型混成同一张图。

JVM 内存分区
┌──────────────────────────────────────────────────────────────┐ │ JVM 内存 │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 栈 (Stack) │ │ 堆 (Heap) │ │ 方法区 │ │ │ │ │ │ │ │ (Method Area) │ │ │ │ 每个线程独立 │ │ 所有线程共享 │ │ │ │ │ │ │ │ │ │ 所有线程共享 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ ┌──────┐ ┌──────┐ ┌──────────────────────┐ │ │ │ 程序 │ │ 本地 │ │ 直接内存 │ │ │ │ 计数器 │ │ 方法栈 │ │ (元空间/堆外内存) │ │ │ └──────┘ └──────┘ └──────────────────────┘ │ └──────────────────────────────────────────────────────────────┘
各区域详解
区域存储内容线程特点
栈 (Stack)局部变量、方法参数、方法调用链线程私有方法调用进栈,结束出栈;栈大小受 JVM 配置影响;栈帧随方法调用建立、返回后移除
堆 (Heap)new出来的对象、数组实例线程共享GC 主要区域,容量大,对象不再从 GC Roots 可达时可被回收,时机由收集器决定
方法区规范层面的类级结构、方法信息和运行时常量池等线程共享HotSpot 中元空间承载类元数据;静态字段值、字符串对象等不能一概归入元空间
程序计数器当前线程执行的字节码行号线程私有极小,记录"执行到哪了"
本地方法栈native方法调用线程私有C/C++ 写的本地方法
方法执行的内存过程
publicclassDemo{publicstaticvoidmain(String[]args){int[]arr=newint[]{10,20,30};// 第 4 行intmax=getMax(arr);// 第 5 行System.out.println(max);}publicstaticintgetMax(int[]array){intmax=array[0];// 第 10 行returnmax;}}
执行过程: 1. 类加载:Demo.class 加载到方法区 2. main 方法被调用 → main 栈帧入栈 3. new int[] → 在堆中创建数组对象 {10,20,30} 4. arr 变量存储在 main 栈帧中,指向堆中的数组地址 5. getMax(arr) → getMax 栈帧入栈,压入 main 之上 6. array 变量存储在 getMax 栈帧中,指向同一个堆地址(地址拷贝) 7. getMax 执行完毕 → getMax 栈帧出栈(销毁局部变量) 8. main 继续执行 println → main 栈帧出栈 → 程序结束

3. 类加载机制

类加载让字节码进入 JVM;初始化执行静态初始化逻辑。对象构造则发生在创建实例时,这几个时机要分开看。

类加载的三个阶段
加载 (Loading) → 连接 (Linking) → 初始化 (Initialization) │ ┌──────────┼──────────┐ │ │ │ ▼ ▼ ▼ 验证 准备 解析
阶段做什么
加载读取.class文件,生成Class对象
验证检查字节码格式、安全性(防止恶意代码)
准备为静态变量分配内存,赋默认值
解析符号引用 → 直接引用(如String→ 实际内存地址)
初始化执行静态变量赋值和静态代码块
类加载器层次(双亲委派模型)
┌──────────────────────────┐ │ Bootstrap ClassLoader │ ← 加载核心类库 (rt.jar → java.base) │ (启动类加载器,C++ 实现) │ └──────────┬───────────────┘ │ 父 ▼ ┌──────────────────────────┐ │ Extension ClassLoader │ ← 加载扩展类库 (jre/lib/ext) │ (扩展类加载器) │ JDK 9+ 被 Platform ClassLoader 替代 └──────────┬───────────────┘ │ 父 ▼ ┌──────────────────────────┐ │ Application ClassLoader │ ← 加载 classpath 下的自定义类 │ (应用类加载器) │ └──────────────────────────┘
双亲委派机制
加载请求 ──→ App ClassLoader → Ext ClassLoader → Bootstrap ClassLoader │ ┌───────────┘ ▼ 能找到?← 是 → 加载 │ ▼ 否 向下委派给子加载器 │ ▼ 都找不到 → ClassNotFoundException

为什么要双亲委派?

  • 安全性:核心类(如java.lang.String)只能由 Bootstrap 加载,防止被恶意替换
  • 避免重复加载:父加载器已加载的类,子加载器不再加载

4. 垃圾回收 (GC) 简介

GC 解决的是内存可回收性,并不负责及时关闭文件和网络连接。对象离开可达关系后何时回收,也不是程序能随口预约的。

什么是 GC?

GC 回收不再从 GC Roots 可达的堆对象;只互相引用、却整体不可达的一组对象也可能被回收。可回收不等于立即回收。

// C/C++ 手动管理int*p=malloc(sizeof(int)*100);// ... 使用 ...free(p);// 必须手动释放,忘了就内存泄漏// Java 自动管理int[]arr=newint[100];arr=null;// 失去引用// JVM 会在适当的时机自动回收 arr 原来指向的内存
堆内存分代
┌───────────────────────────────────────────────┐ │ 堆内存 │ │ ┌─────────────────────┐ │ │ │ 新生代 (Young) │ ← Minor GC 频繁 │ │ │ ┌────┬────┬───────┐ │ │ │ │ │Eden│ S0 │ S1 │ │ 新对象在 Eden 创建 │ │ │ │ │ │ │ │ 存活 → Survivor │ │ │ └────┴────┴───────┘ │ 多次存活 → 老年代 │ │ └─────────────────────┘ │ │ ┌─────────────────────┐ │ │ │ 老年代 (Old) │ ← Major GC 较慢 │ │ │ │ 大对象直接进入老年代 │ │ └─────────────────────┘ │ └───────────────────────────────────────────────┘
区域说明
Eden新对象默认分配区,大部分对象"朝生夕死"
Survivor (S0/S1)Eden 中经过一次 GC 存活的对象进入 Survivor
Old经过多次 GC 仍存活的对象晋升到老年代
MetaSpace (元空间)HotSpot 类元数据区域,使用本地内存,不是堆内的新生代或老年代
GC 算法简介
算法思路特点
标记-清除标记垃圾 → 清除简单但会产生内存碎片
标记-整理标记垃圾 → 存活对象向一端移动无碎片,但移动成本高
复制算法内存分两块,存活对象复制到另一块无碎片、效率高,浪费一半空间(新生代使用)
分代收集常见教学模型以新生代复制、老年代整理说明;并非所有收集器都如此常见收集器会采用分代思想;具体分区和算法取决于收集器

5. 总结

先拿自己写过的程序解释运行过程。需要分析具体问题时,再深入源码和不同收集器。

知识点核心要点
编译阶段javac→ 词法分析 → 语法分析 → 语义分析 → 生成.class
运行阶段类加载 → 字节码校验 → 解释执行 + JIT 编译 → GC 内存管理
栈线程私有,存局部变量和方法调用,方法结束自动释放
堆线程共享,存new出来的对象,GC 主要管理区域
方法区线程共享的类级数据区域;HotSpot 元空间承载类元数据,不能把所有类相关值都画进去
类加载加载 → 连接(验证+准备+解析) → 初始化
双亲委派向上查找,向下加载;安全 + 避免重复加载
GCJVM 管理不再从 GC Roots 可达的堆对象,回收时机不固定,无需手动free
分代回收Eden/Survivor/Old 是常见分代教学模型;具体收集器布局与算法需另行核对
JIT 编译热点代码直接编译为机器码,提升执行速度

看图:运行时区域与 JMM 分开学

这里展示的是规范层面的概念关系,未按实际物理布局或容量比例绘制。元空间属于特定实现的类元数据区域;并发中的 JMM 是另一套可见性与顺序规则。

小练习:把这一节变成自己的代码

写一个带静态初始化、构造器和普通方法的类,先预测打印顺序再运行。

卡住时再打开这条提示

类初始化与每次创建对象不同;不要把静态代码块想成构造器的分身。

这一章怎样算学会了

重新写一个含静态初始化、构造器和普通方法的例子。先猜输出顺序再运行,分别说清类初始化和创建实例发生在什么时机。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/2 1:48:13

字典15个高级用法——写Python不熟字典少只手

字典这种数据结构, 它在日常的开发工作之中是使用频率特别高的一个东西, 可是你觉得自己真的已经把它用得特别顺手了吗, 这篇文章把我们整理出来的15个比较高级的使用方式给列出来了, 它会带着你从比较基础的知识一直学到更进阶的內容, 这样一来就能够让你的字典操作效率直接提…

作者头像 李华
网站建设 2026/11/2 1:47:56

学会Python正则表达式,就看这20个例子(脚本之家修正版)

想要把那些正则表达式的东西给学会, 就直接去看下面这些个例子就行了,总共有20个呢, 这还是一个经过脚本之家这个团队修正过的版本。更新时间被设定在了公元2020年的03月03日那一天的23:45:37, 文章的作者是算法。这篇文章主要介绍了学会正则表达式, 就看这20个例子…

作者头像 李华
网站建设 2026/11/2 1:47:23

wkhtmltopdf 使用指南:把 HTML 网页转成 PDF 的 5 个实用技巧

wkhtmltopdf 使用指南:把 HTML 网页转成 PDF 的 5 个实用技巧 【免费下载链接】wkhtmltopdf Convert HTML to PDF using Webkit (QtWebKit) 项目地址: https://gitcode.com/gh_mirrors/wk/wkhtmltopdf 想在服务器或终端里把网页、报表 HTML 文件转成格式规整…

作者头像 李华
网站建设 2026/11/2 1:47:09

Pearcleaner使用指南:macOS残留文件清理与Mac磁盘瘦身完整教程

Pearcleaner使用指南:macOS残留文件清理与Mac磁盘瘦身完整教程 【免费下载链接】Pearcleaner A free, source-available and fair-code licensed mac app cleaner 项目地址: https://gitcode.com/gh_mirrors/pe/Pearcleaner Pearcleaner是一款免费的macOS清理…

作者头像 李华
网站建设 2026/11/2 1:46:56

为静态资源添加路径前缀并支持发布到 CDN

快速开始欢迎各位进行阅读 Next.js 文档!若是你是刚开始接触 Next.js 的话, 我们这边会向你做出建议, 让你先从这个地方的互动课程开始做起。通过这些带有小测验的互动课程, 你将会学到使用 Next.js 所需的全部知识。如果你有关于 next.js 相关的任何问题, 你可以随…

作者头像 李华