栈是运行时的单位,堆是存储的单位。
JAVA虚拟机栈的概念 每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个栈帧,对应着一次次的方法调用,是线程私有的(生命周 期和线程一致)。主管JAVA程序的运行。 虚拟机栈的特点 1.访问速度快,仅次于程序计数器。 2.JVM直接对栈的操作有两个,及入栈(方法调用)和出栈(方法执行完毕)。 3.对于栈来说不存在垃圾回收。 栈可能出现的异常 1.固定大小的栈(通过-Xss设定)如果线程请求分配的栈空间大于设定的值,会抛出StackOverflowError。 2.动态扩展的栈,在尝试扩展时无法申请到足够的内存,或者在创建新线程时没有足够的内存去创建对应的虚拟机栈,则会报OutOfMemoryError。 栈的存储单位:栈帧 每个线程都有自己的栈,在这个线程上执行的每个方法都各自对应一个栈帧。而且执行引擎运行的所有字节码指令只针对栈顶的那个栈帧进行操作。如果当前方法调用了别的方法,对应的新的栈帧会被创建,并且放到栈顶。栈为线程私有,所以两个线程之间的栈帧无法互相引用。 栈帧当中包含: 1.局部变量表(本地变量表) .局部变量表定义为一个数组,大小在编译时期确定,主要用于存储方法参数和定义在方法体内的局部变量(基本数据类型,对象引用)。 如图,序号0代表this(非静态方法通过对象调用,所以有一个this代表当前对象),序号1为方法参数a,序号2位局部变量b,所以这个方法的局部变量表槽数为3。 局部变量表的最基本存储单元为槽(Slot)32位的,long和double占两个槽(访问时访问第一个槽的索引),其余占一个槽(byte、short、char、boolean都会被转换为int),JVM会为局部变量表中的每一个Slot都分配一个访问索引,通过这个索引即可访问到表中局部变量的值。 在栈帧中,与性能调优关系最为密切的部分就是局部变量表,在方法执行时,虚拟机使用局部变量表完成方法的传递。 局部变量表中的变量也是垃圾回收的重要根节点,只要被局部变量表中直接引用或间接引用的对象都不会被回收。 2.操作数栈(操作“数”的栈) 操作数栈主要用于保存计算过程的中间结果,同时作为计算中变量的临时存储空间。最大深度也在编译期确定,和局部变量表一样long和double也占两个栈单位深度。操作数栈不能通过索引访问,只能入栈和出栈来完成数据防问。 如果被调用的方法有返回值,他的返回值会被压入当前栈帧的操作数栈中,并更新PC寄存器中下一条需要执行的字节码指令。
栈顶缓存:将栈顶元素全部缓存到物理cpu的寄存器中,降低堆内存的读写次数,提升执行引擎的效率。 3.动态链接 在运行时,每个栈帧(方法)中都保存着指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了方法调用过程中的动态链接。在运行时符号引用转换为直接引用(非静态方法,只有在运行时才能确定真正执行的方法,比如只有运行时才能知道真正是哪个子类的方法该被执行),这种转换称之为动态链接。 静态链接:在类加载的链接的解析阶段,对于静态方法会直接由符号引用转换为直接引用。
方法的调用 静态链接和动态链接 早期绑定和晚期绑定 比如一个方法的参数传入了一个父类,这个就是晚期绑定,因为不运行这个方法就不知道传入的是子类还是父类(多态)。非静态方法也是这种,只有在运行时才确定调用它的是父类还是子类。 非虚方法和虚方法 虚拟机提供的方法调用指令
public void show() {
showStatic("atguigu.com");
super.showStatic("good!");
showPrivate("hello!");
super.showCommon();
showFinal();
showCommon();
info();
MethodInterface in = null;
in.methodA();
}
虚方法表 在面向对象的编程中,会频繁的用到动态分配,为了提高性能,每个类都有一个虚方法表(在类的方法去中),存放各个方法的实际入口。虚方法表会在类加载的链接阶段被创建并初始化,类的变量初始值准备完成之后,JVM会把该类的虚方法表也初始化完毕。(不用每次调用一个方法都去寻找,本类,父类,父类的父类这样) dog类自定义了sayhello,重写了tostring 所以这两个存到自己的虚方法表。
CockerSpaniel继承dog类,实现了Friendly接口,重写了sayHello和sayGoodbye方法,所以这两个方法存到自己的虚方法表,而tostring存到dog的虚方法表中。 cat类实现了Friendly接口,重写了sayHello和sayGoodbye方法,重写了object类的finalize方法和tostring方法,自定义了eat方法,所有这5个方法都存到自己的虚函数表中。 动态类型语言和静态类型语言 java是静态类型语言,比如 定义 int a =10;是在编译器就要检查a的类型的。int a = “1”;编译要报错 4.方法返回地址 存放调用该方法的PC寄存器的值 意思就是方法A调用方法B,方法B中存放一条方法A的PC寄存器的值,等待方法B执行完毕之后,B中的方法返回地址可以保证方法A继续准确执行。
5.一些附加信息
|