运行时数据区——Java虚拟机栈

简介:  与程序计数器一样, Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。

 

与程序计数器一样, Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。Java栈以帧为单位保存线程的运行状态。每个方法在执行的时候都会创建一个栈帧用于存储局部变量表、操作栈、动态链接、 方法出口等信息。虚拟机只会直接对Java栈执行两种操作: 以帧为单位的压栈或出栈。

 

Java方法可以以两种方式完成。一种通过return返回,称为正常返回;一种是通过抛出异常而中止的。不管以哪种方式返回,虚拟机都会将当前帧弹出Java栈然后释放掉,这样上一个方法的帧就成为当前帧了。

Java栈上的所有数据都是此线程私有的。任何线程都不能访问另一个线程的栈数据, 因此我们不需要考虑多线程情況下栈数据的访问同步问题。当一个线程调用一个方法时,方法的局部变量保存在调用线程Java栈的帧中 。只有一个线程能总是访问那些局部变量, 即调用方法的线程 。

像方法区和堆一样, Java栈和帧在内存中也不必是连续的 。帧可以分布在连续的栈里,也可以分布在堆里, 或者二者兼而有之。 表示Java栈和栈帧的实际数据结构由虚拟机的实现者决定, 某些实现允许用户指定Java栈的初始大小和最大最小值。

局部变量表

局部变量表存放了编译期可知的各种基本数据类型(boolean、 byte、 char、 short、 int、float、long、 double)、对象引用(reference类型,它不等同于对象本身,根据不同的虚拟机实现, 它可能是一个指向对象起始地址的引用指针, 也可能指向一个代表对象的句柄或者其他与此对象相关的位置) 和 retunAddress类型 (指向了一条字节码指令的地址)。

局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。Java栈帧的局部变量表被组织为一个以字长为单位,从0开始计数的数组。虚拟机通过索引定位的方式使用局部变量表, 编译器首先按声明的顺序把这些参数放入局部变量数组。 字节码指令通过从0开始的索引来使用其中的数据。类型为int、 float、 reference和returnAddress的值在数组中只占据一项, 而类型为byte、 short和char的值在存入数组前都将被转换为int值, 因而同样占据一项。 但是类型为long和double的值在数组中占据连续的两项。

在访问局部变量中的long和double值的时候, 只需取出连续两项中第一项的索引值。 例如某个long值占据第3、 4项, 那么指令会取索引为3的long值。 局部变量区的所有值都是字对齐的, long和double这样占据两项数组元素的值同样可以起始于任何索引 。

下面通过两个例子及图示说明下局部变量区:

public static int runClassMethod(int i,long l,float f,double d,Object o,byte b) { 
   return 0;   
}
public int runInstanceMethod(char c,double d,short s,boolean b) { 
       return 0;   
}

如果是实例方法(非 static的方法), 那么局部变量表中第 0位素引的 Slot默认是用于传递方法所属对象实例的引用, 在方法中可以通过关键字 “this'' 来访问这个隐含的参数。其余参数则按照参数表的顺序来排列,占用从1开始的局部变量Slot。

操作数栈

和局部变量表一样,操作数栈也被组织成一个以字长为单位的数组。可以把操作数栈理解为存储计算时,临时数据的存储区域。虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的。

但和前者不同,它不是通过索引来访问的,而是通过压栈和出栈来访问的。操作数栈的毎一个元素可以是任意的 Java数据类型, 32位数据类型所占的栈容量为1, 64位数据类型所占的栈容量为2。

Java 虚拟机的指令是从操作数栈中而不是从寄存器中取得, 因此它的运行方式是基于栈的而不是基于寄存器的 。

虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据, 执行运算, 然后把结果压回操作数栈。

动态链接

毎个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用, 持有这个引用是为了支持方法调用过程中的动态连接。 Class文件的常量池中存有大量的符号引用, 字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。 这些符号引用一部分会在类加载阶段或第一次使用的时候转化为直接引用,这种转化称为静态解析。 另外一部分将在毎一次的运行期间转化为直接引用 , 这部分为动态连接。

参考:

https://iamjohnnyzhuang.github.io/java/2016/07/12/Java%E5%A0%86%E5%92%8C%E6%A0%88%E7%9C%8B%E8%BF%99%E7%AF%87%E5%B0%B1%E5%A4%9F.html

 

 

 

 

相关文章
|
11月前
|
Linux 虚拟化 iOS开发
VMware Fusion 13.6.4 OEM BIOS 2.7 - 在 macOS 中运行 Windows 虚拟机的最佳方式
VMware Fusion 13.6.4 OEM BIOS 2.7 - 在 macOS 中运行 Windows 虚拟机的最佳方式
1430 3
|
存储 算法 Java
惊!Java程序员必看:JVM调优揭秘,堆溢出、栈溢出如何巧妙化解?
【8月更文挑战第29天】在Java领域,JVM是代码运行的基础,但需适当调优以发挥最佳性能。本文探讨了JVM中常见的堆溢出和栈溢出问题及其解决方法。堆溢出发生在堆空间不足时,可通过增加堆空间、优化代码及释放对象解决;栈溢出则因递归调用过深或线程过多引起,调整栈大小、优化算法和使用线程池可有效应对。通过合理配置和调优JVM,可确保Java应用稳定高效运行。
586 4
|
监控 网络安全 虚拟化
Hyper-V中Win10,虚拟机运行错误处理的方案
当Hyper-V中的Windows 10虚拟机出现运行错误时,可按以下步骤处理:首先进行基本检查与修复,包括检查虚拟机配置、确保Hyper-V服务正常运行及重启相关服务。其次,使用PowerShell命令或DISM工具修复虚拟机配置和系统组件。接着,查看事件查看器中的错误日志,分析问题原因。调整虚拟机资源分配,优化性能。针对特定错误情况,如启动失败或网络问题,采取相应措施解决。若问题仍未解决,考虑克隆、重置或重新安装虚拟机,必要时联系技术支持。操作前请备份重要数据并以管理员身份运行命令。
1423 22
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
1329 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
存储 Dart Java
Dart 虚拟机运行原理
【10月更文挑战第20天】Dart 虚拟机通过一系列复杂的机制和操作,确保 Dart 代码能够准确、高效地执行。它为 Dart 语言的广泛应用提供了坚实的基础和可靠的运行环境
473 6
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
1822 3
|
存储 安全 Java
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程,JDK、JRE、JVM关系;程序计数器,堆,虚拟机栈,堆栈的区别是什么,方法区,直接内存
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别;什么是程序计数器,堆,虚拟机栈,栈内存溢出,堆栈的区别是什么,方法区,直接内存
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程,JDK、JRE、JVM关系;程序计数器,堆,虚拟机栈,堆栈的区别是什么,方法区,直接内存
|
JavaScript Linux 应用服务中间件
如何将Vue项目打包丢入虚拟机CentOS 7中运行
好的,我会按照你的要求,以"这篇文章"四个字开头,用一句话为你摘要每篇文章的主要内容。让我们开始吧。如果你现在就发给我链接,我会立即为你解读。
|
消息中间件 设计模式 安全
多线程魔法:揭秘一个JVM中如何同时运行多个消费者
【8月更文挑战第22天】在Java虚拟机(JVM)中探索多消费者模式,此模式解耦生产与消费过程,提升系统性能。通过`ExecutorService`和`BlockingQueue`构建含2个生产者及4个消费者的系统,实现实时消息处理。多消费者模式虽增强处理能力,但也引入线程安全与资源竞争等挑战,需谨慎设计以确保高效稳定运行。
284 2
|
存储 Java 编译器
Java内存区域与内存溢出异常 - 运行时数据区
【8月更文挑战第2天】Java运行时数据区包括:1) 程序计数器:记录线程执行字节码的行号,线程私有;2) Java虚拟机栈:描述方法执行的内存模型,线程私有,深度过大抛出`StackOverflowError`;3) 本地方法栈:服务于Native方法,线程私有;4) Java堆:所有线程共享,对象实例在此分配内存;5) 方法区:存储类信息、常量等数据;6) 运行时常量池:方法区的一部分,存放字面量和符号引用。不当使用如无限创建对象或过度递归调用会导致各种内存溢出错误。
173 1