JVM(五)运行时数据区之虚拟机栈(Java栈)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了JVM(五)运行时数据区之虚拟机栈(Java栈),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含4667字,纯文字阅读大概需要7分钟。
内容图文
![JVM(五)运行时数据区之虚拟机栈(Java栈)](/upload/InfoBanner/zyjiaocheng/596/12c5992850d44d9d9207e81986ee09ce.jpg)
Java栈示意图
Java虚拟机栈与程序计数器、本地方法栈一样,也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行时,虚拟机都会同步创建一个栈帧(Stack Frame,下面讲解)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用到执行完毕的过程,就对应着一个栈帧的入栈和出栈。
栈帧
栈中的元素称为栈帧,栈帧中存储了方法的局部变量表、操作数栈、动态连接、方法返回地址和一些附加信息。一个栈帧需要分配多少内存,并不会收到程序运行期变量数据的影响,而仅仅取决于程序源码和具体的虚拟机实现的栈内存布局形式。
只有位于栈顶的方法才是在运行的,只有位于栈顶的栈帧才是生效的,其被称为“当前栈帧”(Current Stack Frame),与这个栈帧所关联的方法被称为”当前方法“(Current Method)。
栈帧概念结构图
1.局部变量表
局部变量表是一组变量值的存储空间(数字数组),用于存放方法参数和方法内部定义的局部变量。包括基本数据类型、对象引用、returnAddress类型。所需要的容量大小是在编译期确定下来的。
局部变量表的容量以变量槽(slot)为最小单位,除了long和double需要两个变量槽存储,其他类型(包括引用类型和returnAddress)占用1个变量槽。
Slot重复利用: 栈帧中局部变量表的槽位是可重用的,如果一个局部变量过了其作用域,那么在作用域之后声明的新的局部变量有可能会复用过期局部变量的槽位,从而达到节省资源的目的。这里说的有可能,请看下面代码演示:
/**
* Slot复用对垃圾收集的影响
* 运行参数:-verbose:gc
*/
public class Test2{
public static void main(String[] args) {
{
byte []a=new byte[1024*1024*10];
}
System.gc();
}
}
//[GC (System.gc()) 16594K->11104K(241664K), 0.0057948 secs]
//[Full GC (System.gc()) 11104K->10914K(241664K), 0.0071319 secs]
//可以发现10M的内存并没有清除
/**
* Slot复用对垃圾收集的影响
* 运行参数:-verbose:gc
*/
public class Test2{
public static void main(String[] args) {
{
byte []a=new byte[1024*1024*10];
}
int b=0;
System.gc();
}
}
//[GC (System.gc()) 16594K->912K(241664K), 0.0011176 secs]
//[Full GC (System.gc()) 912K->674K(241664K), 0.0054411 secs]
//此时发现内存已经被回收
数组a能否被回收的根本原因就是:局部变量表中的slot是否还存有关于a数组的引用。第一个案例中,虽然已经超过了a的作用域,但slot还没有被其他变量复用,所以作为GC Roots(垃圾回收篇章详解)的一部分局部变量表仍然保持对它的关联。
Java栈通过索引定位的方式使用局部变量表,索引值从0开始至局部变量表最大的slot数量。如果访问的是32为数据类型的变量,索引N就代表了使用第N个slot。如果是64位数据类型的变量,则会同时使用N和N+1两个slot。
当一个方法被调用时,如果执行的是实例方法(没有被static修饰的方法),那么局部变量表中索引为0的slot默认用于传递方法所属对象实例的引用,在方法中可以通过”this“访问这个隐含的参数。
2.操作数栈
操作数栈也被称为操作栈,同局部变量表一样,操作数栈的最大深度也在编译时就确定下来。操作数栈的每一个元素都可以是包括long和double在内的任意java数据类型。32位所占的栈容量为1,64位为2。
方法刚刚开始执行时,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是入栈和出栈。比如:执行一个整数加法的操作,就是将最接近栈顶的两个元素出栈相加,再将相加的结果重新入栈。
而说Hotspot是基于栈式架构的虚拟机,这个栈指的就是操作数栈。
基于栈的指令集架构优点:
? ①设计和实现更简单,适用于资源受限的系统
? ②避开了寄存器的分配难题:使用零地址指令方式分配
? ③指令流中的指令大部分都是零地址的指令,其执行过程依赖于操作栈。指令集更小,编译器容易实现。
? ④不需要硬件的特殊支持,可移植性好,能够更好的实现跨平台
由于是基于栈的架构,零地址指令方式,所以也有相应的缺点:完成同一个操作需要的更多的出栈和入栈指令,这就意味着将需要更多次的指令分派(instruction dispatch)(后面篇章讲解)次数以及内存读写次数。为了解决这个问题,栈顶缓存技术便出现了。
栈顶缓存: 将栈顶元素全部缓存在物理CPU的寄存器中,以此降低对内存的读写次数,提升执行引擎的执行效率。
3.动态连接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。
Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用保存在class文件的常量池里。比如: 描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。
Class文件中的符号引用一部分会在类加载阶段或者第一次使用的时候就被转化为直接引用,这种转化称为静态解析。另外一部分将在每一次运行期间都转化为直接引用,这部分就称为动态连接(具体过程后面章节讲解)。
4.方法返回地址
当一个方法开始执行后,只有两种方式退出。一是正常返回,二是遇到异常,导致方法退出。方法返回地址了存放调用该方法的pc寄存器的值,方法正常退出时,调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。异常退出时,返回地址要通过异常表(字节码篇章讲解)来确定,栈帧中一般不会保存这部分信息。
5.一些附加信息
《Java虚拟机规范》运行虚拟机实现增加一些规范里没有描述的信息到栈帧中,例如与调试、性能收集相关的信息,这部分却决于具体虚拟机的实现。一般会把动态连接、方法返回地址与附加信息归为一类,称为栈帧信息(帧数据区)。
内容总结
以上是互联网集市为您收集整理的JVM(五)运行时数据区之虚拟机栈(Java栈)全部内容,希望文章能够帮你解决JVM(五)运行时数据区之虚拟机栈(Java栈)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。