JVM核心原理学习笔记
一、Java程序执行流程
Person.java → javac编译 → Person.class → java命令 → JVM加载执行
二、Class文件格式
ClassFile {
u4 magic; // 魔数
u2 minor_version; // 次版本
u2 major_version; // 主版本
u2 constant_pool_count; // 常量池数量
cp_info constant_pool[constant_pool_count-1]; // 常量池
u2 access_flags; // 访问标志
u2 this_class; // 当前类
u2 super_class; // 父类
u2 interfaces_count; // 接口数量
u2 interfaces[interfaces_count]; // 接口表
u2 fields_count; // 字段数量
field_info fields[fields_count]; // 字段表
u2 methods_count; // 方法数量
method_info methods[methods_count]; // 方法表
u2 attributes_count; // 属性数量
attribute_info attributes[attributes_count]; // 属性表
}
三、类加载机制
1. 装载 (Loading)
- 定位: 通过类加载器
findClass(String name)找到类文件位置 - 加载: 类文件字节码 → 方法区
- 创建: 生成对应的Class对象 → 堆内存
2. 链接 (Linking)
- 验证:
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
- 准备: 为静态变量分配内存并设置默认值
static int a = 10;→ 此阶段a=0
- 解析: 将符号引用转换为直接引用
- 将类名、方法名等符号转换为具体内存地址
3. 初始化 (Initialization)
- 执行静态变量赋值和静态代码块
static int a = 10;→ 此阶段a=10
四、类加载器与双亲委派机制
类加载器层次:
Bootstrap ClassLoader (启动类加载器)
↓
Extension ClassLoader (扩展类加载器)
↓
App ClassLoader (应用程序类加载器)
↓
Custom ClassLoader (自定义类加载器)
双亲委派机制:
protected Class<?> loadClass(String name, boolean resolve) {
synchronized (getClassLoadingLock(name)) {
// 1. 检查是否已加载
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// 2. 委托父加载器加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {}
// 3. 父加载器无法加载时,自己加载
if (c == null) {
c = findClass(name);
}
}
return c;
}
}
职责划分:
- Bootstrap: 加载
$JAVA_HOME/jre/lib/rt.jar和-Xbootclasspath指定的包 - Extension: 加载
$JAVA_HOME/jre/lib/ext/*.jar和-Djava.ext.dirs指定的包 - App: 加载classpath指定的类和jar包
- Custom: 应用程序自定义的类加载器(如Tomcat、JBoss等)
五、运行时数据区
线程共享区域:
1. 堆 (Heap)
- 特点: 唯一,线程共享,生命周期与JVM相同
- 存储: 对象实例和数组
- 问题: OutOfMemoryError
2. 方法区 (Method Area) - JDK8+称为元空间(Metaspace)
- 特点: 唯一,线程共享,生命周期与JVM相同
- 存储: 类信息、常量、静态变量、即时编译器编译后的代码
- 注意: JDK8+使用本地内存,不再使用JVM内存
- GC: 会进行垃圾回收(主要回收不再使用的类和常量)
线程私有区域:
3. Java虚拟机栈 (Java Virtual Machine Stacks)
- 特点: 每个线程一个栈
- 组成: 栈帧(每个方法对应一个栈帧)
- 栈帧包含:
- 局部变量表
- 操作数栈
- 动态链接
- 方法返回地址
- 问题: StackOverflowError(如无限递归)
4. 本地方法栈 (Native Method Stacks)
- 为Native方法服务
5. 程序计数器 (Program Counter Register)
- 当前线程执行的字节码行号指示器
六、方法执行过程示例
public static int calc(int op1, int op2) {
op1 = 3;
int result = op1 + op2;
return result;
}
执行过程:
- 创建栈帧,包含局部变量表(op1, op2, result)
- 操作数栈进行运算
- 通过
javap -c可查看字节码指令 - 方法执行完毕,栈帧出栈
七、重要概念区分
JVM内存结构 (运行时数据区)
- 物理上的内存划分
- 包含:堆、方法区、栈、程序计数器等
JMM内存模型 (Java Memory Model)
- 多线程环境下的内存访问规范
- 保证:可见性、原子性、有序性
- 解决线程安全问题
八、总结
- JVM是Java程序的运行环境,理解JVM有助于写出高性能代码
- 类加载机制确保类的正确加载和初始化
- 双亲委派机制保证类的唯一性和安全性
- 运行时数据区管理程序运行时的内存分配
- 栈帧结构体现了方法调用的底层实现
学习建议: 结合javap工具分析字节码,使用JVM参数监控内存使用,深入理解每个环节的实际表现。