虚拟机栈概述
1.1虚拟机栈出现背景
程序员认为JVM内存区:堆heap和栈stack
1.2内存中的栈与堆
栈是运行时的单位,而堆是存储的单位。
即:栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。
堆解决的是数据存储问题,即数据怎么放、放在哪儿。
1.3虚拟机栈基本内容
VMS虚拟机栈:每个线程都会创建一个虚拟机栈;并且每个栈帧Stack Frame对应一个Java方法调用。
是线程私有的。
例子:每个框表示一个方法;每个栈帧都指向一次方法调用;
1.4栈的特点
栈不存在GC;但会OOM;
PC不存在GC,也不存在OOM;
1.5栈的异常
例子:
递归调用,斐波那契数列
1.6设置栈内存大小
2.栈的存储
2.1栈中存储
一个方法与一个栈帧是一一对应的;
2.2栈的运行原理
当前栈帧
例子
线程之间栈是隔离的;
注:method1出现异常后,若main函数进行处理,则main可以正常返回;
若不处理异常,则不会正常返回;
2.3栈帧的内部结构
3.局部变量表(local variables)
本地局部变量表:存储形参,变量等;是用数组来实现的;
3.1局部变量表的字节码
具体看代码,并实践
3.2关于Slot的理解
实例化对象能使用this引用,是因为this存在于局部变量表中;
而静态方法中的局部变量表中,没有this引用;
3.3Slot的重复利用
3.4静态变量与局部变量对比
变量的分类:
1.按数据类型分:基本数据类型,引用数据类型;
2.按照在类中声明的位置:
a.成员变量:在使用前,都经历过默认初始化赋值
类变量:linking的prepare阶段,给类变量默认赋值---> initial阶段:给变量显式赋值,即静态代码块初始化
实例变量:随着对象的创建,会在堆空间中分配实例变量空间,并进行默认赋值
b.局部变量:在使用前,必须要进行显式赋值的。否则,编译不通过;
4.操作数栈(Operand Stack)
栈:可使用数组和链表来实现;但是栈所以只能入栈和出栈;
5.代码追踪
bipush把数据压入操作数栈;
isotre_1把操作数栈中的数据存储到局部变量表;
bipush把数据压入操作数栈;
isotre_1把操作数栈中的数据存储到局部变量表;
iload_1把数据压入到操作数栈;
iadd把操作数栈中两个操作数出栈,并相加;
istore_3把计算的结果保存到局部变量表中;
i++和++i的区别
字节码说明
6.栈顶缓存技术(Top-of-Stack Cashing)
7.动态链接Dynamtic linking
动态链接-指向运行时常量池的方法引用
动态链接就是将符合引用转换为调用方法的直接引用;
#1......符号在常量池中
为什么需要常量池?
常量池的作用,就是为了提供一些符号和常量,便于指令的识别。
8.方法的调用
静态链接:方法在编译期间确定;
动态链接:方法在运行期间确定;
9.虚方法与非虚方法
不能被重写的方法:静态方法,私有方法,final方法,实例构造器,父类方法
invokestatic/invokespecial调用的是非虚方法;
invokevirtual(其余除final修饰的外)/invokeinterface称为虚方法;
invokedynamic指令
动态类型语音和静态类型语音
Python是动态类型语言;
方法重写的本质
从实际类型,往上依次查找
方法调用:虚方法表
举例:
9. 方法返回地址Return Address
正常退出会保存PC的值;若异常退出则不保存了。
10.一些附加信息
栈帧中还允许携带与JAVA虚拟机实现相关的一些附加信息。
例如,对程序调试提供支持信息。
11.栈的相关面试题
ERROR GC
PC 无 无
VMS 有 无
NMS 有 无
堆 有 有
方法区 有 有
局部变量的作用域只在本方法内部则是线程安全的;
外部形参传递进来;作为参数返回回去;这都是不安全的,因为作用域超出本地方法;