走进底层 - JVM工作原理入门指南

发布于:2025-04-10 ⋅ 阅读:(39) ⋅ 点赞:(0)

走进底层 - JVM工作原理入门指南

Java 之所以能够实现“一次编写,到处运行”(Write Once, Run Anywhere, WORA),核心在于 Java 虚拟机(JVM, Java Virtual Machine)。JVM 是 Java 程序的运行环境,负责将 Java 字节码(.class 文件)转换成机器码并执行。

本文将从 JVM 的基本结构内存管理类加载机制垃圾回收(GC) 四个方面,初步了解 JVM 的工作原理。

1. JVM 的基本结构

JVM 主要由以下几个核心部分组成:

  1. 类加载子系统
    • 负责加载 .class 文件到内存,并生成对应的 Class 对象。
  2. 运行时数据区
    • 包括 方法区(Method Area)堆(Heap)虚拟机栈(VM Stack)本地方法栈(Native Method Stack)程序计数器(Program Counter Register)
  3. 执行引擎
    • 负责执行字节码,包括 解释器(Interpreter)即时编译器(JIT Compiler)
  4. 本地方法接口
    • 用于调用 C/C++ 编写的本地库。
      在这里插入图片描述

2. 运行时数据区(内存模型)

JVM 的内存主要分为以下几个部分:

(1) 方法区(Method Area)

  • 用于存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。在 JDK 8 及之后的版本中,方法区使用元空间(Metaspace)来实现,元空间并不在堆内存中,而是使用本地内存。常量池是方法区的一部分,它存储了各种常量,包括字符串常量、基本数据类型常量等

(2) 堆

  • 所有对象实例和数组都在堆上分配,是垃圾回收(GC)的主要区域,也是jvm的最大区域。
  • 分为:
    • 新生代:存放新创建的对象,分为 Eden 区Survivor 区(S0、S1)
    • 老年代:存放长期存活的对象。
    • 元空间(JDK 8+):取代永久代,存储类元数据。

(3) 虚拟机栈

  • 每个线程在运行时都会有一个对应的 Java 虚拟机栈。它由一个个栈帧(Stack Frame)组成,栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。当一个方法被调用时,就会创建一个新的栈帧并压入栈顶,方法执行完毕后,栈帧就会从栈顶弹出。局部变量表用于存储方法中的局部变量,包括基本数据类型和对象引用。操作数栈则用于方法执行过程中的运算操作

(4) 本地方法栈

  • 类似于虚拟机栈,但用于执行 本地方法(如 C/C++ 代码)。

(5) 程序计数器

  • 可以理解为一个指针,它记录了当前线程正在执行的字节码指令的地址。如果当前线程正在执行一个 Java 方法,那么程序计数器中保存的就是正在执行的字节码指令的地址;如果正在执行的是本地方法(Native Method),那么程序计数器的值就是未定义的。由于 Java 是多线程的,每个线程都有自己独立的程序计数器,这样当线程切换时,就能够恢复到正确的执行位置

3. 类加载机制

JVM 加载 .class 文件的过程分为 加载、连接、初始化 三个阶段:

(1) 加载(Loading)

  • 通过 类加载器(ClassLoader) 查找 .class 文件并加载到内存。
  • 类加载器分类:
    • Bootstrap ClassLoader(启动类加载器):加载 JAVA_HOME/lib 下的核心类(如 java.lang.*)。
    • Extension ClassLoader(扩展类加载器):加载 JAVA_HOME/lib/ext 下的扩展类。
    • Application ClassLoader(应用类加载器):加载用户类路径(classpath)下的类。
    • 自定义 ClassLoader:用户可继承 ClassLoader 实现自己的加载逻辑。一般用于服务器的加载器(因为同一个类名不会被反复加载,因此为了防止同类名不同数据的类被省略,需要自定义一个类实现多个对象)

(2) 连接

  • 验证:检查字节码是否符合 JVM 规范。
  • 准备:为静态变量分配内存并赋默认值(如 int 默认 0
  • 解析:将符号引用(如 java.lang.Object)替换为直接引用(内存地址),也就是类名等字符转换为地址

(3) 初始化

  • 执行静态代码块(static {})和静态变量赋值。
  • 采用 双亲委派机制 避免重复加载类。

4. 垃圾回收(GC)

JVM 自动管理内存,通过 垃圾回收器 回收不再使用的对象。

(1) 如何判断对象可回收?

  • 引用计数法(Python 使用):对象被引用时计数+1,为 0 时回收(但 Java 不采用,因为无法解决循环引用问题)。
  • 可达性分析:从 GC Roots(如虚拟机栈、静态变量、本地方法栈引用的对象)出发,标记所有可达对象,不可达的视为垃圾。

(2) 垃圾回收算法

  • 标记-清除:标记垃圾对象后直接清除,但会产生内存碎片。
  • 复制:将存活对象复制到另一块内存(用于 新生代,如 Eden → Survivor)。
  • 标记整理:标记存活对象后整理内存(用于 老年代)。

5. 执行引擎

JVM 执行字节码的方式:

  1. 解释执行:逐行解释字节码,执行速度较慢。
  2. 即时编译(JIT):将热点代码(HotSpot)编译成机器码,提高执行速度

总结

组成部分 核心功能
类加载子系统 加载 .class 文件到内存
运行时数据区 存储类信息、对象、方法调用等
执行引擎 解释或编译执行字节码
垃圾回收(GC) 自动回收无用对象,释放内存

JVM 是 Java 生态的核心,理解其工作原理有助于优化代码、排查内存泄漏(OOM)和提升性能。


网站公告

今日签到

点亮在社区的每一天
去签到