JVM学习-虚拟机栈内部结构

局部变量表

局部变量表也称为局部变量数组和本地变量表

  • 定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据包括各类基本数据类型、对象引用,以及returnAddress类型。
  • 由于局部变量表是建立在线程的栈上,是线程私有数据,因此不存在数据安全问题
  • 局部变量表所需的容量大小是在编译期确定下来的,并保存在方法的Code属性的maxinum local variables数据项中,在方法运行期间是不会改变局部变量表的大小的。
  • 方法嵌套调用的次数由栈的大小决定,一般来说,栈越大,方法嵌套调用次数越多,对于一个函数而言,它的参数和局部变量越多,使得局部变量表膨胀,它的栈帧就越大,以满足方法调用所需传递的信息增大的需求,进而函数调用就会占用更多的栈空间,导致其嵌套调用次数就会减少。
  • 局部变量表中的变量只在当前方法调用中有效,在方法执行时,虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程,当方法调用结束后,随意方法栈帧的销毁,局部变量表也会随之销毁。
import java.util.Date;

/**
 * Administrator
 * 2024/5/16
 */
public class LocalVariablesTest {
    private int count = 0;

    public static void main(String[] args) {
        LocalVariablesTest test = new LocalVariablesTest();
        int num = 10;
        test.test1();
    }

    public void test1() {
        Date date = new Date();
        String name1 = "lotus.com";
        String info = test2(date,name1);
        System.out.println(date+name1);
    }

    public String test2(Date dateP, String name2) {
        dateP = null;
        name2 = "xudiudiu";
        double weight = 140.5;
        char gender = '男';
        return dateP + name2;
    }
    public void test3() {
        count++;
    }
    public void test4(){}
}
代码局部变量表分析
//反编译获取字节码信息
C:\Users\Administrator\IdeaProjects\jvm\target\classes\com\chapter05>javap -v LocalVariablesTest.class
//main方法
 public static void main(java.lang.String[]);
 LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  args   [Ljava/lang/String;
            8       8     1  test   Lcom/chapter05/LocalVariablesTest;
           11       5     2   num   I[integer]

  public void test1();
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      43     0  this   Lcom/chapter05/LocalVariablesTest;
            8      35     1  date   Ljava/util/Date;
           11      32     2 name1   Ljava/lang/String;
           18      25     3  info   Ljava/lang/String;

  public java.lang.String test2(java.util.Date, java.lang.String);
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      33     0  this   Lcom/chapter05/LocalVariablesTest;
            0      33     1 dateP   Ljava/util/Date;
            0      33     2 name2   Ljava/lang/String;
            9      24     3 weight   D(double)
           14      19     5 gender   C(char)

  public void test3();
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lcom/chapter05/LocalVariablesTest;

  public void test4();
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  this   Lcom/chapter05/LocalVariablesTest;
}
SourceFile: "LocalVariablesTest.java"
slot
  • 参数值的存放总是在局部变量数组的index0开始,到数组长度-1的索引结束。
  • 局部变量表,最基本的存储单元是Slot(变量槽)
  • 局部变量表中存放编译期可知的各种基本数据类型(8种),引用数据类型,returnAddress类型的变量。
  • 在局部变量表中,32位以内的类型只占用一个slot(包含returnAddress类型),64位的类型(long和double)占用两个slot
    • byte,short,char在存储前被转换为int,boolean也被转换为int,0表示false,非0表示true
    • long,double则占用两个slot.
  • JVM为局部变量表中的每一个Slot都分配一个访问索引,通过这个索引即可成功访问到局部变量表中指定的局部变量值
  • 当一个实例方法被调用时,它的方法参数和方法体内部定义的局部变量将会按照顺序被复制到局部变量表中的每一个slot上
  • 如果需要访问局部变量表中的一个64bit的局部变量值时,只需要使用前一个索引即可
  • 如果当前帧是由构造方法或实例方法创建的,那么该对象引用this将会存放在index为0的slot处,其余参数按照参数表顺序继续排列
    test1方法的局部变量表,会将this放到index0位置,上图通过IDEA的jclasslib插件可以获得
slot重复利用

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

public class SlotTest {
    public void localVar1() {
        int a = 0;
        System.out.println(a);
        int b = 0;
    }
    public void localVar2() {
        {
            int a = 0;
            System.out.println(a);
        }
        //此时b会复用a的槽位
        int b = 0;
    }
}

槽位复用

静态变量与局部变量对比
  • 参数表分配完毕后,再根据方法体内定义的变量的顺序和作用域分配
  • 变量表有两次初使化的机会,第一次是“准备阶段[prepare]”,执行系统初使化,对类变量设置零值,另一次则是在“初使化[initial]”阶段,赋予程序员在代码中定义的初使值。
  • 和类变量初使化不同的是,局部变量表不存在系统初使化的过程,这意味着一旦定义了局部变量则必须人为的初始化,否则无法使用。
变量
  • 按数据类型分类
    • 基本数据类型
    • 引用数据类型
  • 按声明的位置
    • 成员变量
      • 类变量:第一次是“准备阶段[prepare]”,执行系统初使化,对类变量设置零值,另一次则是在“初使化[initial]”阶段,赋予程序员在代码中定义的初使值。
      • 实例变量:随着对象的创建,会在堆空间中分配 实例变量空间,并进行默认赋值
    • 局部变量:使用之前必须显示赋值,否则编译不通过
//没有赋值不能使用,在编译器中就会报错----Variable 'i' might not have been initialized
public void test() {
        int i;
        System.out.println(i);
    }

在栈帧中,与性能调优关系密切的部分是局部变量表,在方法执行时,虚拟机使用局部变量表完成方法的传递
局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收。

操作数栈(Operand Stack)

  • 每一个独立的栈帧中除了包含局部变量表以外,还包含一个后进先出的操作数栈
  • 操作数栈,在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈和出栈
    • 某些字节码指令将值压入操作数栈,其余的字节码指令将操作数取出栈,使用后再把结果压入栈
    • 比如执行复制、交换、求和等操作
  • 操作数栈,主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。
  • 操作数栈是JVM执行引擎的一个工作区,当一个方法则开始执行的时候,一个新的栈帧也会随之被创建出来,这个方法的操作数栈是空的
  • 每一个操作数栈拥有一个明确的栈深度用于存储数值,其所需的最大深度在编译期就定义好了,保存在方法的Code属性中,为max_stack的值
  • 栈中的任何一个元素都是可以任意Java数据类型
    • 32bit的类型占用一个栈单位深度
    • 64bit的类型占用两个栈单位深度
  • 操作数栈并非采用访问索引的方式来进行数据访问,只能通过标准的入栈和出栈操作来完成一次数据访问
  • 如果被调用的方法带有返回值的话,其返回值将会被压入当前栈帧的操作数栈中,并更新PC寄存器中下一条需要执行的字节码指令
  • 操作数栈中元素的数据类型必须与字节码指令的序列严格匹配,由编译器期间进行验证,同时在类加载过程中的类检验阶段的数据流分析阶段要再次验证
  • Java虚拟机的解释引擎是基于栈的执行引擎,栈即操作数栈
代码追踪
public void testAddOperation() {
       byte i = 15;
       int j = 8;
       int k = i + j;
   }

字节码指令

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public void add() {
       //第1类
       int i1 = 10;
       int i2 = 10;
       i1++;
       ++i2;
       //第2类
       int i3 = 10;
       int i4 = i3++;
       
       int i5 = 10;
       int i6 = ++i5;
       //第3类
       int i7 = 10;
       i7 = i7++;
       
       int i8 = 10;
       i8 = ++i8;
       //第4类
       int i9 = 10;
       int i10 = i9++ + ++i9; 
   }
  • 33
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程简介: 目前,Java是最为流行的编程语言之一,它的基础平台就是JVM。除了Java,如JRuby、Scala、Clojure等语言也运行在JVM平台。熟悉和掌握JVM平台有着重要的实用价值和意义。 在本课程中个,将详细介绍JVM的基本原理、组成以及工作方式,并配合实际案例,介绍相关的调优技巧。 课程大纲: 第一课 初识JVM JVM分类 Java语言规范 JVM规范 介绍JVM的基本知识和发展历史,并介绍了Java语言规范和JVM规范。 第二课 JVM运行机制简介 堆、、方法区等 JVM启动流程 内存模型和volatile实例 解释和编译运行的概念 介绍JVM内部结构、启动流程以及内存模型。并介绍JVM字节码的执行方式。 第三课 常用JVM参数 堆的分配参数 分配及实例讲解 server与client模式 调试跟踪参数 介绍常用的JVM参数,包括内存分配、堆分配、虚拟机运行模式以及调试跟踪参数。 第四课 GC的算法和种类 引用计数 标记清除 复制算法 标记压缩 可触及性 本章是理论性较强的一章,主要介绍GC的基本算法和思想,本章作为下一章节的前序课程。 第五课 GC控制参数 Serial ParNew等GC参数 GC的参数搭配实例分析 介绍GC的设置参数,并分析相关的案列。 第六课 类装载 class装载流程 ClassLoader模式 ClassLoader的使用实例分析 热替换例子 详细介绍ClassLoader的原理和应用。分析2个案例,说明ClassLoader的使用。 第七课 性能监控工具 线程死锁分析 OOM分析 介绍常用的JVM诊断和分析工具,并以死锁和OOM为例,展示这些工具的使用。 第八课 分析Java堆 MAT的使用案例 Jvisualvm介绍使用 介绍了Java堆的分析方法,以一个实例为基础,展示对堆的分析过程。 第九课 锁 baise锁 轻量级锁 自旋锁 介绍JVM中对多线程锁的实现。 第十课 class文件结构 ASM库介绍 介绍JVM规范中的最重要的内容——Class文件结构,同时介绍ASM库的使用以及对class文件的修改。 第十一课 字节码执行 案例以及javap JIT及相关参数 介绍JVM的字节码以及反汇编方法,同时介绍JIT相关的参数和应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值