JVM简图
运行时数据区简图
一、程序计数器(Program Counter Register)
1.程序计数器是什么?
程序计数器是JVM内存模型中的一部分,它可以看作是一个指针,指向当前线程所执行的字节码指令的地址。每个线程在执行过程中都有自己的程序计数器,因此程序计数器是线程私有的,独立于其他线程。
程序计数器不会OOM!!!
2.程序计数器的作用
-
指令执行:在每个线程执行字节码指令时,程序计数器会存储当前正在执行的字节码指令的地址。如果是正在执行本地方法(native method),那么程序计数器的值将是undefined。
-
指令跳转:在字节码指令执行完毕后,程序计数器会自动更新为下一条要执行的字节码指令的地址。通过这种方式,程序计数器可以确保字节码指令按顺序执行。
-
控制流管理:程序计数器帮助管理程序的控制流(如分支、循环、跳转等)。通过更新程序计数器的值,可以实现各种控制流指令(如if、for循环、switch等)的跳转逻辑。
-
多线程切换:由于Java是多线程的语言,每个线程都有自己独立的程序计数器。当线程切换时,程序计数器会保存当前线程的执行位置,当线程再次被调度时,程序计数器会恢复到之前保存的位置,以确保线程可以继续从正确的位置执行。
二、虚拟机栈(Java Virtual Machine Stack)
在Java虚拟机(JVM)中,每个线程在创建时都会创建一个虚拟机栈,虚拟机栈是每个线程私有的数据区,用于管理方法调用和执行。其内部保存一个个的栈帧(Stack Frame),对应着一次次的Java方法调用。每当一个线程调用一个方法时,JVM会为该方法创建一个新的栈帧(Stack Frame)并将其压入虚拟机栈中,方法执行完毕后,栈帧会从栈中弹出。
**存在OOM,不需要垃圾回收**
栈帧(Stack Frame)
1、JVM直接对Java栈的操作只有两个,就是对栈帧的压栈和出栈,遵循“先进后出”/“后进先出”原则。在一条活动线程中,一个时间点上,只会有一个活动的栈帧。
2、只有当前正在执行的方法的栈帧(栈顶栈帧)是有效的,这个栈被称为当前栈帧(Current Frame),与当前栈帧相对应的方法就是当前方法(CurrentMethod),定义这个方法的类就是当前类(CurrentClass)。
3、执行引擎运行的所有字节码指令只针对当前栈帧进行操作。如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,放在栈的顶端,成为新的当前帧。
1.栈帧的组成部分
- 局部变量表(Local Variable Array/Table)
- 操作数栈(Operand Stack)
- 动态链接(Dynamic Linking)
- 方法返回地址(Return Address)
- 附加信息(Additional Information)
2.详细描述
1. 局部变量表(Local Variable Array/Table)
- 原理:局部变量表是一个数组,用于存储方法的局部变量,包括方法参数和方法内部定义的变量。
- 作用:为每个方法提供存储和访问局部变量的空间。局部变量通过索引进行访问,索引从0开始。例如,
int a = 10;
中的a
就存储在局部变量表中。 - 存储信息:存储了方法的参数和方法内部定义的局部变量。可以存储各种数据类型,包括基本数据类型(int、float、long、double等)以及对象引用。
2. 操作数栈(Operand Stack)
- 原理:操作数栈是一个LIFO栈,用于字节码指令执行时的临时存储空间。
- 作用:在方法执行过程中,用于保存中间计算结果、传递参数以及存储返回值。例如,执行加法操作
i + j
时,会将i
和j
压入操作数栈,执行完加法操作后,将结果存储在操作数栈中。 - 存储信息:方法执行过程中临时存储的操作数、中间计算结果。
3. 动态链接(Dynamic Linking)
- 原理:每个栈帧包含指向运行时常量池的方法引用,,方法的具体指针信息。
- 作用:当方法被调用时,动态链接会将符号引用转换为实际的内存地址。例如,调用一个方法时,会将该方法在常量池中的符号引用转换为实际的方法地址。
4. 方法返回地址(Return Address)
- 原理:在方法调用时,返回地址会记录调用方法的指令地址,以便方法返回时能找到正确的返回位置。
- 作用:方法执行完毕后,返回到调用该方法的地方继续执行。这个地址一般是调用方法的下一条指令。
5. 附加信息(Additional Information)
- 原理:附加信息因JVM实现而异,包括栈帧的一些其他信息,比如调试信息和性能分析信息。
- 作用:为JVM提供更多的运行时信息支持,如异常处理信息、JVM优化信息等。