文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JVM学习- - -虚拟机栈详解

2023-09-12 20:34

关注

前言:今天学长带领大家走进JVM学习,让我们一起来学习认识虚拟机栈吧~

目录

1 虚拟机栈概述

虚拟机栈出现的背景

初步印象

内存中的栈和堆

虚拟机栈基本内容

栈的优点

2 栈的存储单位

栈中存储什么?

栈运行原理

栈帧的内部结构

3 局部变量表

关于Slot的理解

Slot的重复利用

举例:静态变量与局部变量的对比

补充说明

4 操作数栈(Operand Stack)

5 代码追踪

6 栈定缓存 (Top-of-Stack Cashing)技术

7 动态链接(Dynamic Linking)

动态链接(或指向运行时常量池的方法引用)

8 方法的调用

早期绑定 & 晚期绑定

虚方法和非虚方法

方法的调用:关于invokedynamic指令

动态类型语言和静态类型语言

Java 语言中方法重写的本质

方法的调用:虚方法表

9 方法返回地址 (Return Address)

10 附加信息

11 栈的相关面试题


1 虚拟机栈概述

虚拟机栈出现的背景

由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的。

优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令。

初步印象

有不少Java开发人员一提到Java内存结构,就会非常粗粒度地将JVM中的内存区理解为仅有Java堆(heap)和Java栈(stack)?为什么?

内存中的栈和堆

栈是运行时的单位,而堆是存储的单位

即:栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放、放在哪儿。

虚拟机栈基本内容

实例:

public void methodA() {  int i = 10;  int j = 20;}​public void methodB() {  int k = 30;  int m = 40;}

一个栈帧对应一个方法

栈的优点

面试题:开发中遇到的异常有哪些?

栈中可能出现的异常

设置栈内存大小

我们可以使用参数-Xss 选项来设置线程的最大栈空间,栈的大小直接决定了函数调用的最大可达深度。

代码测试

// 默认情况下打印 9810// 设置 -Xss256k// 打印 2239static int count = 1;public static void main(String[] args) {  System.out.println(count++);  main(args);}

2 栈的存储单位

栈中存储什么?

栈运行原理

栈帧的内部结构

每个栈帧中存储着:

3 局部变量表

关于Slot的理解

Slot的重复利用

栈帧的局部变量表的槽位是可以重用的,如果一个局部变量过了其作用域,那么在其作用域之后申明的新的局部变量就很有可能会复用过期局部变量的槽位,从而达到节省资源的目的

举例:静态变量与局部变量的对比

public void test() {    int i;    System.out.println(i)}

这样的代码是错误的,没有赋值不能够使用。

补充说明

4 操作数栈(Operand Stack)

理论

5 代码追踪

public void testAddOperation() {  byte i = 15;  int j = 8;  int k = i + j;}

使用javap命令反编译class文件:javap -v 类名.class

 0 bipush 15 2 istore_1 3 bipush 8 5 istore_2 6 iload_1 7 iload_2 8 iadd 9 istore_310 return

代码转成字节码

int m = 8; -> bipushint m = 800; -> sipush

常见i++和++i的区别,

6 栈定缓存 (Top-of-Stack Cashing)技术

前面提过基于栈式架构的虚拟机所使用的零地址指令更增加紧凑,但完成一项操作的时候必然需要使用更多的入栈和出栈命令,这同事也就意味着需要更多的指令分派(instaruction dispatch)次数和内存读/写次数。

由于操作数是存储在内存中的,因此频繁地执行内存读/写操作必然会影响执行速度。为了解决这个问题,HotSpot JVM的设计者们提出了栈顶缓存(Tos, Top-of-Stack Cashing)技术,将栈顶元素全部缓存在物理CPU的寄存器中,以此降低对内存的读/写次数,提升执行引擎的执行效率

7 动态链接(Dynamic Linking)

动态链接(或指向运行时常量池的方法引用)

问题:为什么要有常量池?

常量池的作用,就是为了提供一些符号和常量,便于指令的识别。

8 方法的调用

在JVM中,将符号引用转换为调用方法的直接引用与方法的绑定机制相关。

早期绑定 & 晚期绑定

对应方法的绑定机制为:早起绑定(Early Binding)和晚期绑定(Late Binding)。绑定的是一个字段、方法或者类在符号引用被替换为直接引用的过程,这仅仅发生一次。

虚方法和非虚方法

随着高级语言的横空出世,类似与Java一样的基于面向对象的编程语言如今越来越多,尽管这类编程语言在语法风格上存在一定的差别,但是它们彼此之间始终保持着一个共性,那就是都支持封装、继承和多态等面向对象特性,既然这一类的编程语言具备多态特性,那么自然也就具备早期绑定和晚期绑定两种方式。

Java中任何一个普通的方法其实都具备虚函数的特征,它们相当于C++语言中的虚函数(C++ 中需要使用关键字virtual来显式定义)。如果在Java程序中不希望某个方法拥有虚函数的特征时,则可以使用关键字final来标记这个方法。

非虚方法:

  • 如果方法在编译期就确定了具体的调用版本,这个版本在运行时是不可变的。这样的方法称为非虚方法

  • 静态方法、私有方法、final方法、实例构造器、父类方法都是非虚方法。

  • 其他方法称为虚方法

多态的使用前提:

  1. 类的继承关系

  2. 方法的重写

虚拟机中提供了一下几条方法调用指令:

  • 普通调用指令:

    1. invokestatic:调用静态方法,解析阶段确定唯一方法版本

    2. invokespecial: 调用方法、私有父类方法,解析阶段确定唯一方法版本

    3. invokevirtual:调用所有虚方法

    4. invokeinterface:调用接口方法

  • 动态调用指令:

    1. invokedynamic: 动态解析出需要调用的方法,然后执行

前四条指令固话在虚拟机内部,方法的调用执行不可认为干预,而invokedynamic指令则支持由用户确定方法版本。其中invokestatic指令和invokespecial指令调用的方法称为非虚方法,其中的(final修饰的除外)称为虚方法。

方法的调用:关于invokedynamic指令

  • JVM字节码指令集一直比较稳定,一直到Java7中才增加了一个invokedynamic指令,这是Java为了实现【动态类型语言】支持而做的一种改进

  • 但是在Java7中并没有提供直接生成invokedynamic指令的方法,需要借助ASM这种字节码工具来产生invokedynamic指令。直到Java8的Lambda表达式的出现,invokedynamic指令的生成,在Java中才有了直接的生成方式

  • Java7中增加的动态语言类型支持的本质是对Java虚拟机规范的修改,而不是对Java语言规则的修改,这一块相对来讲比较复杂,增加了虚拟机的方法调用,最直接的受益者就是运行在Java平台的动态语言的编译器。

动态类型语言和静态类型语言

动态类型语言和静态类型语言两者的区别就在于对类型的检查是在编译期还是在运行期,满足前者就是静态类型语言,反之就是动态类型语言。

说的直白一点就是,静态类型语言是判断自身的类型信息;动态类型语言是判断变量的类型信息,变量没有类型信息,变量值才有类型信息,这是动态语言的一个重要特征。

Java 语言中方法重写的本质

  1. 找到操作数栈顶的第一个元素所执行的对象的实际类型,记作C。

  2. 如果在类型C中找到与常量中的描述复核简单名称都相符的方法,则记性访问权限校验,如果通过则返回这个方法的直接引用,查找过程接结束;如果不通过,则返回java.lang.IllegalAccessError异常。

  3. 否则,按照集成关系从下往上依次对C的各个父类进行第2步的搜索和验证过程。

  4. 如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。

IllegalAccessError介绍:

程序视图访问或修改一个属性或调用一个方法,这属性或方法,你没有权限访问。一般的,这个会引起编译异常。这个错误如果发生在运行时,就说明一个类发生了不兼容的改变。

方法的调用:虚方法表

  • 在面向对象的编程中,会很频繁的使用到动态分派,如果在每次动态分派的过程中都要重新在类的方法元数据中搜索合适的目标的话就可能影响到执行效率。因此,为了提高性能,JVM采用在类的方法区建立一个虚方法表(Virtual method table)(非虚方法不会出现在表中)来实现。使用索引表来代替查找

  • 每个表中都有一个虚方法表,表中存放着各个方法的实际入口。

  • 那么虚方法表什么时候被创建?

    虚方法表会在表加载的链接阶段被创建并开始初始化,类的变量初始值准备完成之后,JVM会把该类的方法表也初始化完毕。

举例1

在调用Son的toString()方法的时候,如Son类和Father类都没有重新,会直接调用Object类的toString方法。

举例2

对于CockerSpaniel的虚方法表来说,sayHello()、sayGoodbye()是调用自身,toString()方法是调用Dog类,其他调用Object类

9 方法返回地址 (Return Address)

  • 存放调用该方法的pc寄存器的值

  • 一个方法的结束,有两种方法:

    • 正常执行完成

    • 出现未处理的异常,非正常退出

  • 无论通过哪种方式退出,在方法退出后都返回到该方法被调用的位置。方法正常退出时,调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。

  • 本质上,方法的退出就是当前栈帧出栈的过程。此时,需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置PC寄存器值等,让调用者方法继续执行下去。

    正常完成出口和异常完成出口的区别在于:通过异常完成出口退出的不会给他的上层调用者产生任何的返回值。

当一个方法开始执行后,只有两种方式可以退出这个方法:

  1. 执行引擎遇到任意一个方法返回的字节码指令(return),会有返回值传递给上层的方法调用者,简称正常完成出口

    • 一个方法在正常调用完成之后究竟需要使用哪个返回指令还需要根据方法返回值的实际数据类型而定。

    • 在字节码指令中,返回指令包含ireturn(当返回值是boolean、byte、char、short和int类型时使用)、ireturn、fretuen、dreturn、以及areturn,另外还有一个return指令供声明为void的方法、实例初始化方法、类和接口的初始化方法使用。

  2. 在方法执行的过程中遇到了异常(Exception),并且这个异常没有在方法内进行处理,也就是只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出。简称异常完成出口

方法执行过程中抛出异常时的异常处理,存储在一个异常处理表,方便在发生异常的时候找到处理异常的代码。

异常表

  Exception table:     from    to  target type         0     6     9   Class java/lang/Exception

如果从0~6的字节码位置发生Exception异常,由字节码位置9处理

10 附加信息

栈帧中还允许携带与Java虚拟机实现相关的一些附加信息。例如,对程序调试提供支持的信息。

11 栈的相关面试题

  1. 举例栈溢出的情况?(StackOverflowError)

    通过-Xss设置栈的大小;

  2. 调整栈的大小,就能保证不出现溢出吗?

    不能,如果本身就是死循环的代码,栈的大小不论多大都会溢出

  3. 分配的栈内存越大越好吗?

    不是,可能就挤占了其他区域的内存空间

  4. 垃圾回收是否会涉及到虚拟机栈?

    垃圾回收不会涉及到虚拟机栈

  5. 方法中定义的局部变量是否线程安全?

    局部变量是线程安全的

来源地址:https://blog.csdn.net/m0_63722685/article/details/128576653

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯