JVM 内存结构总览(运行时内存结构)
JVM 在运行 Java 程序时,会将内存划分为多个区域,用于执行、存储类信息、对象、方法调用栈等。
按照 Java 虚拟机规范,JVM 的内存区域可以细分为 程序计数器
、虚拟机栈
、本地方法栈
、堆
、方法区
等。
其中 方法区
和 堆
是线程共享的,虚拟机栈
、本地方法栈
和 程序计数器
是线程私有的。
本地方法栈都说是本地了肯定就是线程私有,虚拟机栈既然是要执行一些方法那么肯定也是线程自己私有进行的,程序计数器是指当前执行到哪一步了肯定也是特有的;方法区存的是类的信息等等,堆存储的所有对象实例和数组,都是属于线程共享的内容。
各个区域的详细介绍
1、程序计数器(Program Counter)
- 作用:记录当前线程正在执行的字节码指令地址,表示执行到哪一步。
- 特点:每个线程都有一个(线程私有)
- 意义:支持多线程间的上下文切换
2、虚拟机栈(JVM Stack)
Java 虚拟机栈的生命周期与线程相同。
当线程执行一个方法时,会创建一个对应的 栈帧
,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,然后栈帧会被压入栈中。当方法执行完毕后,栈帧会从栈中移除。
栈帧是 JVM 运行时栈的基本单位,存储方法执行所需的变量和计算结果,方法调用时入栈,执行完毕后出栈。
在这里插入图片描述
3、本地方法栈(Native Method Stack)
本地方法栈与虚拟机栈相似,区别在于虚拟机栈是为 JVM 执行 Java 编写的方法服务的,而本地方法栈是为 Java 调用本地(native) 方法服务的,由 C/C++ 编写。
在本地方法栈中,主要存放了 native 方法的局部变量、动态链接和方法出口等信息。当一个 Java 程序调用一个 native 方法时,JVM 会切换到本地方法栈来执行这个方法。
运用场景:
当 Java 应用需要与操作系统底层或硬件交互时,通常会用到本地方法栈。
比如调用操作系统的特定功能,如内存管理、文件操作、系统时间、系统调用等。
举例:System.currentTimeMillis()
就是调用本地方法来获取操作系统的当前时间。
再比如 JVM 自身的一些底层功能也需要通过本地方法来实现。像 Object 类中的 hashCode()
方法、clone()
方法等。
解释一下 native 方法:
Native 方法是在 Java 中通过 native 关键字声明的,用于调用非 Java 语言(如 C/C++)编写的代码。Java 可以通过 JNI(Java Native Interface)与底层系统、硬件设备、或高性能的本地库进行交互。
使用本地方法栈(Native Method Stack)的主要目的是与操作系统底层或硬件交互,而 native 方法通常使用 C/C++ 主要是为了更高效地访问底层资源。
4、堆(Heap)
堆被所有线程共享,在 JVM 启动时创建,主要存放所有 new 出来的对象实例。
Java 中“几乎”所有的对象都会在堆中分配,堆也是 垃圾收集器(GC)
管理的目标区域。
从内存回收的角度来看,由于垃圾收集器大部分都是基于分代收集理论设计的,所以堆也会被划分为 新生代
、老年代
、Eden空间
、From Survivor空间
、To Survivor空间
等。
但随着
JIT 编译器
的发展和逃逸技术的逐渐成熟,“所有的对象都会分配到堆上”就不再那么绝对了。
JIT(Just-In-Time)编译器,即即时编译器,是 JVM(Java 虚拟机)中的动态优化组件,用于将字节码转换为机器码,提升 Java 代码的执行效率。
从 JDK 7 开始,JVM 已经默认开启逃逸分析了,如果某些方法里的对象无法逃逸(被方法体外使用)出去,那么对象可以直接在栈上分配内存,但是会逃逸的对象一定要分配到堆中,因为堆是公共的,而栈是私有的。
5、方法区/元空间(MetaSpace)
方法区并不真实存在,属于 Java 虚拟机规范中的一个逻辑概念,用于存储已被 JVM 加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等。
在 HotSpot 虚拟机中,方法区的实现称为永久代(PermGen),但在 Java 8
及之后的版本中,已经被元空间(Metaspace)所替代。
变量存在堆栈的什么位置?
对于局部变量来说,它存储在当前方法的栈帧中的局部变量表中。当方法执行完毕,栈帧被回收,局部变量也会被释放。
栈帧是 JVM 运行时栈的基本单位
public void method() {
int localVar = 100; // 局部变量,存储在栈帧中的局部变量表里
}
对于静态变量来说,它存储在 Java 规范中的方法区中,也就是元空间(Metaspace)。
public class StaticVarDemo {
public static int staticVar = 100; // 静态变量,存储在方法区中
}
内存区域组成部分 | 共享/私有 | 作用 |
---|---|---|
虚拟机栈 | 私有 | 线程开始执行一个任务时,会创建一个栈帧,其中包括局部变量等数据,压入栈内,当任务执行完毕过后弹出栈。 |
本地方法栈 | 私有 | 与虚拟机栈类似,不过会在需要和操作系统或底层硬件交互的时候使用,并且虚拟机栈使用的 Java,而本地方法栈使用的是 C/C++。 |
程序计数器 | 私有 | 表示当前程序执行到了哪一步。 |
方法区/元空间 | 共享 | JVM 的虚拟逻辑概念,存储类信息、常量、静态变量、被 JVM 加载的代码缓存。 |
堆 | 共享 | 主要存储 new 出来的对象实例。 |