Java基础 - JVM

什么是JVM

Java的理念是:Write Once, Run Anywhere。Java编译器把 Java 程序编译成“字节码”(.class 文件),字节码可以在所有平台运行,这是JAVA特性之一。字节码是 JVM 能理解的“中间语言”,JVM可以把字节码翻译成你的电脑能理解的机器指令。因此只需要在不同的机器上安装适配不同架构的JVM,就可以实现 Write Once, Run Anywhere。

JVM的功能

  • 跨平台运行
  • 内存管理:JAVA帮助开发者垃圾回收,分配内存
  • 性能优化:即时编译器(Just-In-Time ,JIT ),将热点代码(频繁执行的代码段)编译成本地机器码,以提高程序的执行性能
  • 安全性:异常处理,类加载时字节码验证,沙箱环境等

JVM与OS进程的关系

  1. JVM 本身就是一个普通的操作系统进程:JVM就是一个跑在 OS 之上的用户态程序(C/C++ 写的),当你执行 java Main 时,操作系统启动了一个名为 java 的可执行文件。这个文件是编译好的机器码(主要是 C++ 写成的 HotSpot 虚拟机)。

  2. Java 程序不是进程,JVM 进程里运行着 Java 程序。JVM 启动后,读取你的 .class 文件,把它视为“输入数据”,解析这些数据里的指令。

  3. Native Memory (本地内存):这是操作系统分配给 JVM 进程的总内存地址空间。

  4. Java Heap:JVM 向 Native Memory 申请并管理的最大一块连续内存区域,用于存储所有的 Java 对象实例和数组。

  5. Java Stack (VM Stack):存储 栈帧 (Stack Frame)。每个 Java 方法被调用时,都会创建一个栈帧,用来存放局部变量表(基本类型值、对象引用)、操作数栈、动态链接等。

  6. Native Method Stack:用于支持 native 方法(使用 C/C++ 编写的方法,如 JNI 调用)执行的栈。JVM 本身通常由 C++ 编写,当 Java 代码调用操作系统底层功能(如文件读写、网络 IO)时,必须通过 JNI (Java Native Interface) 进入 Native 层。

  7. JVM 的 Garbage Collection 功能是由 GC 线程实现的。GC 线程本质上是 JVM 内部的 C++ 线程。

JVM的运行时数据区域

以JDK 1.8为例,

  • 线程私有的:
    • 程序计数器:当前线程执行的字节码位置
    • Java 虚拟机栈:每个线程的JAVA方法的“方法调用栈”,包含栈帧
    • 本地方法栈:给调用 C、C++ 等非 Java 方法(native 方法)用的栈
  • 线程共享的
    • 堆:用来存储所有 Java 对象实例,由垃圾回收器(GC)统一管理
    • 元空间:用来存储类的元数据(类的信息、常量、静态变量、方法字节码等)。

类加载器

类加载器是指把类的字节码(通常是 .class 文件)加载到 JVM 的内存中,并生成相应的 Class 对象的工具。具体来说,类加载器把字节码(.class 文件)加载到 JVM 的方法区(Method Area)(Java 8 之后是 元空间(Metaspace)),然后在堆中创建一个 Class 对象来存储类的信息,方便反射操作。

类并不是一开始就全部加载。类加载器可以按需加载:当你第一次使用某个类时,它才去加载它。

public class Person {
    static List<String> list = new ArrayList<>();

    static {
        System.out.println("Person 类被初始化");
    }

    int age = 20;

    public void sayHello() {
        System.out.println("Hello");
    }
}

根据以上的例子,类加载器执行过程:

  1. 加载:类加载器通过类的名字 Person 找到 Person.class 文件,将 .class 文件的字节码加载到 JVM,并在堆中生成一个代表这个类的 java.lang.Class 对象作为数据访问的入口
  2. 验证:检查字节码是否合法
  3. 准备:为静态变量(List<String> list)分配内存,并赋初始值 null(此时静态变量只是占位,还没初始化)
  4. 解析:将符号引用替换为直接引用,比如将System.out.println方法名解析为真正的内存地址
  5. 初始化:执行静态代码块,给静态变量赋值(这里的list变量本身在方法区,指向的HashMap对象实例在堆里创建)

Class对象

public final class Class {
    private Class() {}
}

比如,当JVM加载String类时,会执行Class cls = new Class(String);在堆中创建Class对象。这个对象包含类名、包名、父类、实现的接口、所有方法、字段等对应class的所有信息。内存分布:

Java 堆:
┌────────────────────────────┐
│ Class<Person> clazz        │
│ ── name: "Person"          │
│ ── loader: AppClassLoader  │
│ ── klass_pointer →────────────┐
└────────────────────────────┘  │
                                |
方法区(Metaspace):              ▼
┌────────────────────────────┐
│ Class Metadata (Klass*)    │
│ ── className: "Person"     │
│ ── superClass: java.lang.Object
│ ── interfaces: ...         │
│ ── methods[]: sayHello()   │
│ ── fields[]: name          │
│ ── constantPool: ...       │
│ ── annotations: ...        │
└────────────────────────────┘

JAVA对象在内存中的结构

Java 对象几乎都在堆上,引用变量通常在栈上。

  1. 对象头
    包含:

    • 标记字段(Mark Word):指向哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID等。
    • 类型指针(Klass pointer):指向类的元数据,是一个在元空间的C++结构,与上文Class对象的klass_pointer指向的结构一样。
  2. 实例数据(成员变量,如果是 Primitive Type 则保存值本身,如果是 Reference Type 则保存对象地址)

  3. 对齐填充(JVM 要求对象大小必须是 8 的倍数)


Java基础 - JVM
http://example.com/2025/07/04/JAVA_basis/java-jvm/
Author
Songlin Zhao
Posted on
July 4, 2025
Licensed under