堆栈背景:
由于是跨平台型的设计,所以java的命令都是基于堆栈设计的。由于不同平台的cpu体系结构不同,所以不能作为基于寄存器的命令来设计;
优点是跨平台,指令集小,编译器容易实现。 缺点是性能下降,实现同样的功能需要更多的指令
堆栈和堆栈
堆栈是运行时的单位,堆是存储的单位
堆栈解决程序的执行问题,即程序如何执行,或者如何处理数据。堆栈解决的是数据存储的问题,即数据如何放置,放在哪里;
堆栈是什么
每个线程在创建时创建一个虚拟机堆栈,其中包含各自的堆栈帧,并对应于这次的java方法调用
的存储单元:堆栈帧中,线程上的每个方法都对应于堆栈帧。 堆栈帧是一个内存块,是一个数据集,它保存方法正在运行的各种数据信息
堆栈生命周期
匹配线程的生命周期
堆叠作用:
负责java程序的执行,保存方法的局部变量(8种基本数据类型,对象的引用地址)、部分结果,并参与方法的调用和返回
1 .局部变量成员变量(或属性)
2 .基本数据类型引用数据类型(类、数组、接口) )。
堆栈的优点:
先进先出(FILO ) )。
堆栈是一种快速有效地分配存储的方法,访问速度仅次于程序计数器
jvm直接操作java堆栈的只有两个:
1 .按方法执行,伴随有instack (instack、press stack )
2 .执行结束后的堆栈
对堆栈来说没有垃圾回收的问题
1 .没有GC问题,但可能发生OOM问题(pc寄存器中没有两者的问题) )。
堆栈可能发生的异常:
根据java虚拟机规范,java堆栈的大小可以是动态的或恒定的
1 .如果使用固定大小的java虚拟机堆栈,则可以在创建线程时独立选择每个线程的java虚拟机堆栈容量。 如果线程请求分配的堆栈容量超过了java虚拟机允许的最大容量,java虚拟机将抛出堆栈溢出错误异常。 递归深度太深的情况等。
2 .如果Java虚拟机是动态可扩展的,并且在尝试扩展时无法请求足够的内存,或者没有足够的内存来创建与入侵新线程时对应的虚拟机堆栈,则Java虚拟机将在outoutmenoryError
设置堆栈大小
jvm参数-Xss设置堆栈的最大空间,由堆栈大小决定
/**演示堆栈中的异常:堆栈溢出错误
*@authorshkstart
* @create 2020下午9:08
*
*默认值: count : 11420
*设置堆栈大小:-Xss256k : count : 2465*/
publicclasstackerrortest { privatestaticintcount=1; publicstaticvoidmain (字符串[ ] args ) {
system.out.println(count );
出局;
主(args );
}
}
堆栈的内部结构:
1 .局部变量表
局部变量数组或局部变量表:主要存储方法参数和方法中定义的局部变量,包括基本数据类型。 对象参照、返回值、存储的单元在变量时隙(slot,长度32比特以内为时隙,64比特在2时隙使用索引比特的开头索引),本地变量表的容量大小在编译后决定;
$堆栈框架中与性能优化关系最密切的部分是局部变量表,虚拟机在执行方法时使用局部变量表完成方法传递。 局部变量中有无变量决定了有无指针,并影响堆的垃圾回收
$局部变量表中的变量也是重要的垃圾回收根节点,除非回收了在局部变量表中直接或间接引用的对象,否则垃圾回收算法具有较高的根搜索算法
2 .方法返回地址
保存调用此方法的pc寄存器的值。
一种方法的结尾有两种方法:
成功执行完成
发生了未处理的异常,异常结束了
无论以哪种方式退出,方法结束后都会返回调用该方法的位置。 方法成功结束时,被调用pc计数器的值为返回地址,即调用方法的指令的下一个指令的地址。 另一方面,在异常结束的情况下,返回地址由异常表确定,一般不在堆栈帧中保存该信息的一部分。
本质上,方法的结束是当前堆栈退出的过程。 在这种情况下,必须继续执行调用方方法,如将顶级方法的局部变量表、操作数堆栈和返回值推入调用方堆栈框架的操作数堆栈,设置PC寄存器值。
正常完成出口和异常完成出口的区别在于,异常完成出口退出时,他的上层调用方不会产生任何返回值。
3 .操作数堆栈
堆栈:可以使用数组或链表实现
操作数堆栈在方法执行过程中,根据字节码指令向堆栈写入或提取数据。 即,是instack和out stack
一些字节码指令将值推入操作数堆栈
余的字节码指令将操作数取出栈.使用他们后在将结果压入栈比如复制,交换,求和等操作
·操作数栈,主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。
·操作数栈就是JVM执行引擎的一个工作区,当一个方法刚开始执行的时候,一个新的栈帧也会随之被创建出来,这个方法的操作数栈是空的。
·每一个操作数栈都会拥有一个明确的栈深度用于存储数值,其所需的最大深度在编译期就定义好了,保存在方法的Code属性中,为max_stack的值。
·栈中的任何一个元素都是可以任意的Java数据类型。
32bit的类型占用一个栈单位深度
64bit的类型占用两个栈单位深度
·操作数栈并非采用访问索引的方式来进行数据访问的,而是只能通过标准的入栈(push)和出栈(pop)操作来完成一次数据访问。
.如果被调用的方法带有返回值的话,其返回值将会被压入当前栈帧的操作数栈中,并更新Pc寄存器中下一条需要执行的字节码指令。
·操作数栈中元素的数据类型必须与字节码指令的序列严格匹配,这由编译器在编译器期间进行验证,同时在类加载过程中的类检验阶段的数据流分析阶段要再次验证。
·另外,我们说Java虚拟机的解释引擎是基于栈的执行引擎,其中的栈指的就是操作数栈。
/*程序员面试过程中, 常见的i++和++i 的区别,放到字节码篇章时再介绍。*/
public voidadd(){//第1类问题:
int i1 = 10;
i1++;int i2 = 10;++i2;//第2类问题:
int i3 = 10;int i4 = i3++;int i5 = 10;int i6 = ++i5;//第3类问题:
int i7 = 10;
i7= i7++;int i8 = 10;
i8= ++i8;//第4类问题:
int i9 = 10;int i10 = i9++ + ++i9;
}
4.动态链接(指向运行时常量池的动态引用)
每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用"包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接(Dynamic Linking)。比如:invokedynamic指令
在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用(Symbolic Reference)保存在class文件的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。
5.一些附加信息
栈帧中还允许携带与Java虚拟机实现相关的一些附加信息。例如,对程序调试提供支持的信息。