JVM内存与垃圾回收之虚拟机栈(内部细节)

虚拟机栈

首先明确的是虚拟机栈是会出现OOM的,就是内存溢出,因为栈的大小有限,但是GC是不会对栈进行操作的,也就是栈不会进行垃圾回收。

虚拟机栈的概述

栈解决的是程序的运行问题,即程序如何运行,堆解决的的是数据存储的问题,,即数据放在哪儿,怎么放的问题。
Java虚拟机栈早期也叫做JAVA栈,每个线程在创建的时候都会拥有一份虚拟机栈。它的生命周期和线程是一样的,它保存方法的局部变量,方法返回地址,以及参与方法的返回和调用。

栈的特点

  • 栈是一种有效的分配存储方式,运行速度仅次于程序计数器
  • JAVA堆虚拟机栈的操作只有两个入栈跟出栈。
  • 因为没有数据的处理,只有入栈跟出栈的操作,所以也不会进行垃圾回收。

栈中可能出现的异常

StackOverflowError:如果采用固定大小的Java虚拟机栈。如果线程请求分配的内存大于了Java虚拟机栈允许的最大容量,就会出现此异常。

OOM(OutOfMemory):虚拟机栈在动态扩展的时候,没有足够的内存去创建就会发生OOM。

栈的存储单位

栈中存储什么?
每个线程都有独立的栈,栈中的数据格式是以栈帧的形式存放的。
在这个线程上每一个执行方法,都对应一个栈帧。
栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种信息。

栈运行原理

不同线程中所包含的栈帧是不允许存在相互引用的,即不可能在一个栈帧之中引用另外一个线程的栈帧
如果当前方法调用了其他方法,方法返回的时候,当前栈帧会传回此方法的执行结果给前一个栈帧,接着虚拟机会丢掉当前栈帧,使得前一个栈帧重新称为当前栈帧。(递归的原理
Java中两种返回,一种是return,另外一种是抛出异常。不管哪种方式,都会导致栈帧被弹出。


栈帧的内部结构

在这里插入图片描述
如上图所示,栈帧中一共有五个信息,其中一些附加信息,不是很重要。对于局部变量表,操作数栈,动态链接,方法返回地址在接下来详细阐述。

局部变量表 (Local Variables)

  1. 局部变量表也被称之为本地变量表或局部变量数组。
  2. 它被定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型,对象引用以及returnAddress类型。
  3. 由于局部变量表是建立在线程的栈上,是线程私有的数据,因此不存在数据安全问题
  4. 局部变量表索需要的容量大小实在编译器确定下来的。

其中 returnAddress 数据只存在于字节码层面,与编程语言无关,也就是说,我们在 Java 语言中是不会直接与 returnAddress 类型的数据打交道的。
returnAddress 类型的值是指向字节码的指针,

对于 JVM 来说,程序就是存储在方法区的字节码指令,而 returnAddress 类型的值就是指向特定指令内存地址的指针。

关于slot的理解

  • 参数值总是放在局部变量数组的index0的位置上,到数组长度-1的索引结束
  • 局部变量表,最基本的单位就是Slot 即槽。
  • 在局部变量表中。32位以内的类型只占用一个slot,64的long和double占两个槽位。
  • JVM会为局部变量表中的每一个Slot都分配一个访问索引,通过这个索引可以成功访问到局部变量表中指定的局部变量值。
  • 当一个实例方法被调用的时候,它的方法参数和方法体内部定义的局部变量将会展昭顺序被复制到局部变量表中的每一个Slot上
    当前方法的引用存放在index0的位置上,如果是静态方法则不存在引用,因为是类调用

静态变量与局部变量对比

  • 类变量在linking的prepare阶段,给变量赋默认值。在initial阶段,给类变量显示赋值。

  • 实例变量:随着对象的创建,会在堆空间中分配实例变量空间,并进行默认初始化。

  • 局部变量:在使用前,必须要进行显式赋值的。否则,编译不通过。

  • 补充:在栈帧中,与性能调优关系最密切的部分就是前面提到的局部变量表。在方法执行时,虚拟机使用局部变量表完成方法的传递。

  • 局部变量表中的表里也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收。

操作数栈(Operand Stack)

概念

作用:主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。

执行引擎的工作区就是在操作数栈,当一个方法刚开始执行的时候,一个新得栈帧被创建出来,这个方法的操作数栈是空的。
操作数栈并非采用访问索引的方式来进行数据访问,只能通过入栈出栈来完成一次数据访问。

Java虚拟机的解释引擎就是基于栈的执行引擎,栈值得就是操作数栈

栈顶缓冲技术

由于操作数栈是存储在内存中的,因此频繁地执行内存读、写操作必然会影响执行的速度。为了解决这个问题,HotSpot虚拟机设计者们体重了栈顶缓存技术,将栈顶元素全部缓存在物理cpu的寄存器中,以此降低对内存的读、写次数,提升执行引擎的执行效率。

动态链接

栈帧数据区:动态链接。
每一个栈帧内部都包含一个执行运行时常量池中该栈帧所属方法的引用,包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接。

在Java原文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用保存在class文件的常量池里。 动态链接的作用就是为了将这些符号引用转换为调用方法的直接应用。

动态链接和晚期绑定都是因为被调用的方法在编译器无法被确定下来,只能够在程序运行时根据实际的类型绑定相关的方法,穿的是引用。

关于虚方法和非虚方法

非虚方法:如果方法在编译器就确定了具体的调用版本,这个版本在运行时是不可变的,这样的方法陈伟废墟方法。
静态方法,私有方法,final方法,实例构造器,父类方法都是非虚方法,其他都是虚方法。

静态方法,私有方法,finnal方法不能进行方法的重写。

方法返回地址

指的是当前方法返回的时候,返回给上一个栈帧的地址。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值