【JVM】4.运行时数据区(程序计数器、虚拟机栈)

2023-12-18 05:48:50

文章目录

    • 4.JVM的运行时数据区
      • 4.1 程序计数器
      • 4.2 Java虚拟机栈
      • 4.3 虚拟机栈内存溢出

4.JVM的运行时数据区

4.1 程序计数器

程序计数器(PC)会记录着下一行字节码指令的地址。执行完当前指令后,PC刷新,JVM的执行引擎根据程序计数器执行下一行指令。

在多线程环境下,Java虚拟机需要通过程序计数器记录CPU切换前解释执行到哪一句指令并继续解释执行。

在这里插入图片描述

4.2 Java虚拟机栈

Java虚拟机栈使用栈的数据结构来管理方法调用中的基本数据。每一个方法的调用使用一个栈帧(Stack Frame)来进行保存。方法调用就入栈,方法结束就出栈。程序一开始的时候,虚拟机栈是空的,程序结束的时候虚拟机栈也是空的。

Java虚拟机栈随着线程的创建而创建,回收会在线程的销毁时执行,由于方法可能可能会在不同线程中执行,所以每个线程都有自己的虚拟机栈

栈帧是如何组成的?

在这里插入图片描述

  • 局部变量表

栈帧中的局部变量表是一个数组,数组中的每一个位置称为槽。long和double类型占用两个槽,其他类型(包括引用类型)占用一个槽。

局部变量表保存的内容有:实例方法的this对象,方法的参数,方法体中声明的局部变量。按照顺序进行保存。

对于字节码文件来说,其局部变量表的信息就很详细。记录了每个变量的编号(第几个变量),PC开始位置,以及长度(其实就是作用域,PC可以访问多长的位置,超过这个长度就不能使用这个变量了),在数组中的索引(序号),变量的名字。

在这里插入图片描述

  • 操作数栈和帧数据

操作数栈在编译期就可以确定操作数栈的最大深度,从而在执行时正确地分配内存大小。

帧数据在不同的虚拟机中有不同的内容。不过有一些核心内容是一样的。比如动态链接、方法出口、异常表的使用。

动态链接:当前类的字节码指令引用了其他类的属性或者方法时,需要将符号引用(编号)转换成对应的运行时常量池中的内存地址,动态链接就保存了编号到运行时常量池的内存地址的映射关系。所以动态链接保存了映射关系。

方法出口:方法结束时,当前栈帧会从虚拟机栈中弹出,同时PC应该指向上一个栈帧中下一条指令的地址,所以当前栈帧中需要保存此方法出口的地址。

异常表:存放代码中异常的处理信息,包含了try代码块和catch代码块执行后跳转到的字节码指令位置。

Java中还有一类方法,是由c/c++实现的,称为本地方法。这些方法会放在哪里呢?

在这里插入图片描述

4.3 虚拟机栈内存溢出

当栈帧的数量过多,内存占用超过了虚拟机栈的默认内存大小,就会出现栈内存溢出(StackOverflow)。最典型的场景——无限递归就会导致栈内存溢出。

栈具有默认大小,win中虚拟机栈大小不同的系统可能会不同。

在这里插入图片描述

可以通过-Xss这个参数来设置虚拟机栈的大小。

在这里插入图片描述

显然,栈帧中局部变量过多,会引起局部变量表过大,操作数栈深度变大,也会影响一个栈帧的大小,从而影响虚拟机栈能够存放栈帧的个数。但是一般情况下,栈帧都不会达到几千个,所以可以手动把虚拟机栈内存调小点,节省内存。

在这里插入图片描述

文章来源:https://blog.csdn.net/weixin_52808620/article/details/134936860
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。