JVM学习记录

最近想要复习一下Java的体系知识,所以记录了JVM的学习。

文章目录

1.编译

JVM中运行的是.class文件,那么如何将.java文件转换为.class文件呢,这一过程就叫做编译。那又是怎么编译的呢?

复制代码
源码文件 (HelloWorld.java)
        ↓ 执行javac命令
┌─────────────────────────────────────────┐
│       词法分析 (Lexical Analysis)        │
│ 源代码字符流 → 标记(Token)流              │
└─────────────────────────────────────────┘
    我的理解就是识别"单词",识别常量名、变量名、关键字等标识符,
因为Java的标识符是有规则的,首字符必须为字母、下划线。
那这个检查就是在这个阶段的
        ↓
┌─────────────────────────────────────────┐
│       语法分析 (Syntax Analysis)         │
│ 标记流 → 抽象语法树(AST)                 │
└─────────────────────────────────────────┘
	这个阶段就是识别语法,像英语一样,Java也有自己的语法,你不能
违背语法。
        ↓
┌─────────────────────────────────────────┐
│      语义分析 (Semantic Analysis)       │
│ 类型检查、符号解析、语法树标注           │
└─────────────────────────────────────────┘
	语义分析是检查代码的逻辑含义是否正确的过程。语法正确的代码不一定有意义,语义分析就是找出那些"通顺但不合理"的代码。
        ↓
┌─────────────────────────────────────────┐
│   注解处理 (Annotation Processing)      │
│ 处理编译时注解,生成新代码           │
└─────────────────────────────────────────┘
        ↓
┌─────────────────────────────────────────┐
│  语法树转换 (AST Transformations)       │
│ 去除语法糖、内联常量、优化等             │
└─────────────────────────────────────────┘
        ↓
┌─────────────────────────────────────────┐
│      生成字节码 (Bytecode Generation)    │
│ AST → 字节码指令                         │
└─────────────────────────────────────────┘
        ↓
┌─────────────────────────────────────────┐
│      字节码优化 (Bytecode Optimization)  │
│ 简化、压缩、优化字节码                   │
└─────────────────────────────────────────┘
        ↓
       .class 文件

2.运行

现在已经生成了.class文件,使用java命令可以对这个文件进行运行,那运行的机理又是怎样的呢?

2.1完整流程

复制代码
执行 java HelloWorld
    ↓
操作系统启动JVM进程
    ↓
JVM初始化(创建JVM实例)
    ↓
├─ 1. 类加载
│   ├─ 加载(Loading)
│   ├─ 链接(Linking)→ 验证、准备、解析
│   └─ 初始化(Initialization)
│
├─ 2. 运行时数据区
│   ├─ 方法区(Method Area)
│   ├─ 堆(Heap)
│   ├─ 虚拟机栈(Stack)
│   ├─ 程序计数器(PC Register)
│   └─ 本地方法栈(Native Stack)
│
├─ 3. 执行引擎
│   ├─ 解释器
│   ├─ JIT编译器
│   └─ 垃圾回收器
│
└─ 4. 本地库接口

2.2操作系统启动JVM进程

首先需要在操作系统上开辟一个进程,具体的执行如下:

复制代码
1. 解析命令:java是JRE的可执行程序
2. 创建进程:
   - 分配内存空间
   - 创建进程控制块(进程控制块(Process Control Block,简称PCB)就像是一个进程的"身份证"+"档案"+"病历"。PID就在这里面)
   - 设置初始堆栈(例如下面的命令)
 java -Xms512m           # 初始堆大小
     -Xmx1024m          # 最大堆大小
     -Xss256k           # 线程栈大小
     -cp .              # 类路径
     HelloWorld         # 主类
     arg1 arg2          # 命令行参数
3. 加载JVM:
   - 加载jvm.dll(Windows)或 libjvm.so(Linux)
   - 初始化JVM

2.3JVM初始化

JVM的初始化,首先会调用JNI方法创建一个JVM实例,然后才会实现JVM的初始化。

2.3.1类加载

JVM初始化后,就如何加载要class文件呢,这个过程被成为类加载。具体的流程如下:

复制代码
.class文件(硬盘上)
    ↓
[1. 寻找文件] → 在哪些地方找.class文件?
    ↓
[2. 读取字节] → 把.class文件读取到内存
    ↓
[3. 解析格式] → 解析成ClassFile结构
    ↓
[4. 创建Class对象] → 在堆中创建Class对象
    ↓
[5. 放入方法区] → 存储类的元信息
    ↓
准备好被使用!
  • 寻找文件

这里面体现如何寻找文件,会从以下四个加载器中加载,至于加载的机制被称为双亲委派机制。

复制代码
1. 启动类加载器 (Bootstrap ClassLoader)
   搜索路径: $JAVA_HOME/jre/lib/*.jar
   查找顺序: rt.jar → jsse.jar → jce.jar → ...

2. 扩展类加载器 (Extension ClassLoader)
   搜索路径: $JAVA_HOME/jre/lib/ext/*.jar
   加载: javax.xml, javax.swing, javax.crypto等扩展类

3. 应用程序类加载器 (Application ClassLoader)
   搜索路径: CLASSPATH环境变量 或 -cp参数指定的路径
   加载: 你自己的类,如com.example.MyClass

4. 自定义类加载器
   搜索路径: 自定义的任何地方

双亲委派机制如下:

复制代码
你要加载 com.example.MyClass
    ↓
应用类加载器收到请求
    ↓
"我先不加载,让我爸爸(扩展类加载器)试试"
    ↓
扩展类加载器收到请求
    ↓
"我也让我爸爸(启动类加载器)试试"
    ↓
启动类加载器收到请求
    ↓
1. 在rt.jar中找com/example/MyClass.class
2. 找不到!返回null
    ↓
扩展类加载器自己尝试
    ↓
1. 在ext/*.jar中找com/example/MyClass.class
2. 找不到!返回null
    ↓
应用类加载器自己尝试
    ↓
1. 在CLASSPATH中找com/example/MyClass.class
2. 找到!加载成功
相关推荐
xian_wwq1 小时前
【学习笔记】威胁情报
网络·笔记·学习
小糊涂加油1 小时前
TypeScript学习笔记
笔记·学习
@游子1 小时前
Python学习笔记-Day6
笔记·python·学习
xunyan62341 小时前
面向对象(下)-模版方法的设计模式其应用场景
java·学习·设计模式
Yweir1 小时前
Linux性能监控的工具集和分析命令工具
java·linux·jvm
四维碎片1 小时前
【Qt】QTimer 学习笔记总结
笔记·qt·学习
组合缺一1 小时前
Solon AI 开发学习16 - generate - 生成模型(图、音、视)
java·人工智能·学习·ai·llm·solon
Mr_Hu4042 小时前
鸿蒙开发学习笔记-生命周期小记
笔记·学习·harmonyos·鸿蒙
友莘居士2 小时前
深入浅出:以太坊虚拟机(EVM)存储模型设计与权衡
jvm·区块链·虚拟机·solidity·evm·合约调用