深入浅出JVM内存结构

作为一名 Java 开发者,你可能天天听到 JVM,但你是否真正思考过:我们写好的代码,到底是怎么被计算机执行的?各种对象和变量又是如何分配在内存中的?

今天这篇博客,我们就从宏观到微观,把 JVM 的核心架构和内存结构彻底串联起来,帮你建立一个整体的认知。

一、初识JVM:到底什么是Java虚拟机?

在深入内存之前,我们先回忆一下什么是JVM。

JVM是Java程序的运行环境 ,它不直接运行我们编写的Java源码,而是承担三大核心职责:执行字节码、管理程序运行内存、自动垃圾回收

二、JVM如何运行一个Java程序?

整个流程依托JVM的四大核心组件协同工作:**类加载子系统、运行时数据区(内存结构)、执行引擎、本地方法接口.**在介绍具体的执行流程之前,先介绍一下这几个组件的功能。

  • **类加载子系统:**它负责把 .class 字节码文件,从硬盘读到 JVM 内存里,并变成可以运行的 Java 类。

  • **运行时数据区:**JVM 的内存区域,用来存数据、存代码、存方法调用。

  • **执行引擎:**把字节码变成能运行的指令然后执行,进行垃圾回收。

  • 本地方法接口:让 Java 调用底层 C/C++ 写的本地方法。

运行流程:

JVM 运行程序的过程串起来看,其实就是一个 "加载 → 存储 → 执行 → 调用系统能力" 的过程。

首先,我们编写的 Java 代码会经过编译器编译成 .class 字节码文件。这个字节码并不能直接被操作系统执行,因此程序启动时,JVM 会先通过类加载系统把需要用到的 Class 文件加载到内存中。

类加载完成后,类的元数据信息,比如类名、父类、接口、字段、方法等,会被存放到运行时数据区中的方法区

接下来程序开始运行。运行过程中创建的对象会被分配到 中;方法调用时会创建栈帧并压入虚拟机 ;每个线程还会有一个程序计数器 ,用于记录当前执行到了哪条字节码指令,以便在线程切换后能够继续从正确的位置执行。如果代码调用了 Native 方法,那么对应的方法执行信息会进入本地方法栈

此时,运行时数据区已经准备好了程序执行所需的数据,但这些字节码本身仍然不能直接运行。因此 JVM 的执行引擎会开始工作,它负责将字节码翻译成当前操作系统和 CPU 能识别的机器指令,然后交给 CPU 执行。

三、核心重点:JVM五大内存结构深度解析

我们可以将五大区域分为两类:线程私有内存线程共享内存,这是理解内存特性的关键。

线程私有阵营(生命周期与线程相同)

  • 程序计数器(PC Register)

    • 作用:记录当前线程正在执行的下一条字节码指令的地址,解释器据此读取指令。

    • 为什么私有? 多个线程并发执行时,CPU 会频繁切换线程。为了让线程切换回来后能准确找到上次执行到哪了,每个线程必须有一个独立的计数器。

  • 虚拟机栈(JVM Stack)

    • 作用 :主管 Java 方法的调用。每次方法调用都会创建一个栈帧(包含局部变量表、操作数栈、动态链接、方法返回地址等),方法执行完即出栈销毁。
  • 本地方法栈(Native Method Stack)

    • 作用 :与虚拟机栈类似,专门为 JVM 调用底层 C/C++ 的 Native 方法服务。

线程共享阵营(全程序可见,涉及垃圾回收)

  • 堆(Heap)

    • 作用 :存储所有的对象实例和数组,是 JVM 中最大的一块内存,也是垃圾回收(GC)绝对的核心战场。
  • 方法区(Method Area)

    • 作用 :存储已被虚拟机加载的类的元数据信息(全类名、父类、字段/方法信息、静态变量等)。

    • 历史演进(面试高频)

      • JDK 1.8 之前 :由永久代(PermGen)实现,占用的是 JVM 内存。因为大小有限,加载类过多时极易引发 OOM(内存溢出)。字符串常量池在 JDK 1.7 之前也在这里。

      • JDK 1.8 之后 :彻底废除永久代,改用元空间(Metaspace)实现。元空间直接使用本地内存(操作系统内存) ,只要系统内存够大,几乎不会再因加载类过多而 OOM。此时,字符串常量池被移到了堆中。

四 JVM四大引用类型

背景与问题

JDK 1.2 之前其实只有一种引用关系,也就是我们平时使用的强引用。当时对象的状态只有两种:有引用就存活,没有引用就回收。

但这种机制过于简单,无法满足实际业务场景的需求。例如缓存中的对象,我们希望内存充足时保留,内存紧张时再释放;又或者某些对象被回收时,希望能够收到通知并执行资源清理工作。

因此 JVM 引入了强引用、软引用、弱引用和虚引用四种引用类型,本质上是为垃圾回收器提供更细粒度的对象生命周期管理能力。

不同的引用强度代表对象不同的存活优先级:

  • 强引用表示对象必须存活;

  • 软引用表示内存不足时可以回收;

  • 弱引用表示只要发生 GC 就可以回收;

  • 虚引用则用于在对象被回收时接收通知,执行额外的资源释放操作。

所以四种引用解决的核心问题就是:

让对象的回收不再只有"回收"和"不回收"两种状态,而是能够根据业务场景灵活控制对象的生命周期和回收时机,从而实现更精细化的内存管理。

强引用

平常 Object obj = new Object() 这种就是强引用。 只要强引用存在,GC 永远不会回收这个对象,哪怕内存溢出 OOM 也不会动它;只有手动把引用置为 null、跳出作用域断开引用,对象才会被回收。

软引用--解决缓存问题

描述有用但不是必须的对象,内存充足时 GC 不会回收;一旦堆内存不够、快要 OOM 之前,会把所有软引用对象全部回收释放内存。

弱引用

只要触发 GC,不管内存够不够,弱引用指向的对象立刻被回收。

虚引用

**虚引用不是为了访问对象,而是为了在对象被回收时收到通知然后做一些善后工作。**通常会配合引用队列使用,当对象被判定为可回收时,JVM会将对应的虚引用加入引用队列,程序可以监听这个队列并执行额外的资源清理工作。例如堆外内存、文件句柄、Socket连接等。

相关推荐
AC赳赳老秦2 小时前
OpenClaw+AWS 深度应用:自动生成 CloudFormation 模板、批量管理 S3 存储桶
java·python·面试·职场和发展·php·deepseek·openclaw
Zik----3 小时前
保研英语面试
面试·职场和发展
Tenaryo3 小时前
从 178ms 到 1ms:当 Store-to-Load Forwarding 卡住你的 for 循环
后端·面试
折哥的程序人生 · 物流技术专研3 小时前
Java 23 种设计模式:从踩坑到精通 | 适配器模式 —— 让不兼容的接口也能一起工作
java·设计模式·面试·适配器模式·单一职责原则
想要成为糕糕手4 小时前
JavaScript 异步编程完全指南
javascript·面试·promise
二十画~书生4 小时前
【LED 点阵驱动】- 面试高频问题全解
面试·职场和发展
回眸&啤酒鸭4 小时前
【回眸】低压电工证培训记录
职场和发展
JAVA面经实录9174 小时前
ZooKeeper 面试题完整标准答案(面试背诵版)
分布式·zookeeper·面试
cfm_29144 小时前
JVM对象创建与内存分配机制深度解析
jvm