1.栈的存储单位
栈是运行时单位,而堆是存储的单位
栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储问题,即数据怎么放,放在哪儿
java虚拟机栈
早期也叫java栈,每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧,对应着一次次的java方法调用,是线程私有的
生命周期和线程一致
主管java程序的运行,保存方法的局部变量、部分结果,并参与方法的调用和返回
栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息。
一条活动线程中,一个时间点上,只会有一个活动的栈帧;
执行引擎运行的所有字节码指令只针对当前栈帧进行操作。
栈帧中存储:
局部变量表
操作数栈(表达式栈)
动态链接(指向运行时常量池的方法引用)
方法返回地址(方法正常或异常退出的定义)
一些附加信息
2.局部变量表
也称为局部变量数组或本地变量表
定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型,对象引用
由于局部变量表是建立在线程的栈上,是线程的私有数据,因此不存在数据安全问题
局部变量表所需的容量大小是在编译期确定下来的
关于slot的理解
参数值的存放总是在局部变量数组的index0开始,到数组长度-1的索引结束
局部变量表,最基本的存储单元是slot
局部变量表中存放编译期可知的各种基本数据类型(8种)、引用类型
在局部变量表里,32位以内的类型只占用一个slot,64位的类型占用两个slot
byte、short、char在存储前被转换为int,boolean也被转换为int,0表示false,非0表示true
jvm为局部变量表中的每一个slot都分配一个访问索引,通过索引即可成功访问到局部变量表中指定的局部变量值
当一个实例方法被调用的时候,它的方法参数和方法体内部定义的局部变量将会按照顺序被复制到局部变量表中的每一个slot上
如果需要访问局部变量表中一个64bit的局部变量值,只需要使用前一个索引即可
如果当前帧是由构造方法或实例方法创建的,那么该对象引用this将会存放在index为0的slot处,其余的参数按照参数顺序继续排列。
栈帧中的局部变量表中的槽位是可以重用的:如果一个局部变量过了其作用域,那么在其作用域
之后申明的新局部变量就很有肯能会复用过期局部变量的槽位,从而达到节省资源的目的;
局部变量:在使用前,必须要进行显示赋值,否则,编译不通过
局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收。
3.操作数栈
操作数栈主要用于保存计算过程的中间结果, 同时作为计算过程中变量临时存储空间;
操作数栈就是jvm执行引擎的一个工作区,当一个方法刚开始执行的时候,一个新的栈帧就会随之被创建出来,这个方法操作数栈是空的
每一个操作数栈都会拥有一个明确的栈深度用于存储数值,其所需的最大值深度在编译期就定义好了,保存在方法的code属性中,为max_stack的值
栈中的任何一个元素都可以是java的数据类型
32bit的类型占用一个栈单位深度
64bit的类型占用两个栈单位深度
操作数栈并非采用访问索引的方式来进行数据访问的,而是只能通过标准的入栈和出栈操作来完成一次数据访问
5.代码追踪
6.栈顶缓存技术
将栈顶的元素全部缓存在物流cpu的寄存器中,以降低对内存的读/写次数,提升执行引擎的执行效率
7.动态链接
每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。
动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用
常量池的作用
提供一些符合和常量,便于指令的识别
8.方法的调用:解析与分派
动态类型语言和静态类型语言
区别在于对于类型的检查是在编译期还是在运行期,静态类型语言是在编译器,动态类型是在运行期
方法重写的本质
1.找到操作数栈顶的第一个元素所执行的对象实际类型,记做C
2.如果在类型C中找到与常量中的描述符合简单名称都相符的方法,则进行访问权限验证,如果通过,则返回这个方法的直接引用,查找过程结束;如果不通过,则返回java.lang.IllegalAccessError异常
3.否则,按照继承关系从下往上依次对各个父类进行第2步的搜索和验证过程
4.如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常
IllegalAccessError:程序试图访问或修改一个属性或调用一个方法,这个属性或方法,你没有权限访问,一般的,这个会引起编译器异常,这个错误如果发生在运行时,就说明一个类发生了不兼容的改变。
虚方法表
在面向对象的编程中,会频繁的使用到动态分派,如果在每次动态分派的过程中都要重新在类的元方法中搜索合适的目标的话就肯能影响到执行效率,为了提高性能,jvm在类的方法区建立一个虚方法表来实现,使用索引表来代替查找
每个类都有一个虚方法表,表中存放着各个方法实际入口
虚拟方法表什么时候被创建?
虚方法表会在类加载的链接阶段被创建并开始初始化,类的变量初始值准备完成之后,jvm会把该类的方法表也初始化完毕。
非虚方法
9.方法返回地址
帧数据区:方法返回地址、动态链接、一些附加信息
方法返回地址
存放调用该方法的pc寄存器的值
一个方法的结束,有两种方式:
1.正常执行完成
2.出现未处理的异常,非正常退出
无论哪种方式退出,在方法退出后都返回到该方法被调用的位置,方法正常退出时,调用者的pc计数器的值作为返回地址;异常退出时,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息
正常退出和异常完成出口的区别:通过异常完成出口退出的不会给他的上层调用者产生任何的返回值
11.面试题