开始改变第一天 JVM的原理到调优(1)

101 阅读3分钟

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;
}

执行过程:

  1. 创建栈帧,包含局部变量表(op1, op2, result)
  2. 操作数栈进行运算
  3. 通过javap -c可查看字节码指令
  4. 方法执行完毕,栈帧出栈

七、重要概念区分

JVM内存结构 (运行时数据区)

  • 物理上的内存划分
  • 包含:堆、方法区、栈、程序计数器等

JMM内存模型 (Java Memory Model)

  • 多线程环境下的内存访问规范
  • 保证:可见性、原子性、有序性
  • 解决线程安全问题

八、总结

  • JVM是Java程序的运行环境,理解JVM有助于写出高性能代码
  • 类加载机制确保类的正确加载和初始化
  • 双亲委派机制保证类的唯一性和安全性
  • 运行时数据区管理程序运行时的内存分配
  • 栈帧结构体现了方法调用的底层实现

学习建议: 结合javap工具分析字节码,使用JVM参数监控内存使用,深入理解每个环节的实际表现。