一些收藏

JVM栈- 局部变量表

2022-06-12  本文已影响0人  程序员札记

简介

虚拟机栈的出现背景

由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的【如果设计成基于寄存器的,耦合度高,性能会有所提升,因为可以对具体的CPU架构进行优化,但是跨平台性大大降低】。
优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令。
内存中的栈与堆
首先栈是运行时的单位,而堆是存储的单位。
即:栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放,放哪里

image.png

虚拟机栈基本内容

Java虚拟机栈是什么?

Java虚拟机栈(Java Virtual Machine Stack),早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的Java方法调用,栈是线程私有的
虚拟机栈的生命周期

生命周期和线程一致,也就是线程结束了,该虚拟机栈也销毁了

虚拟机栈的作用

主管Java程序的运行,它保存方法的局部变量(8 种基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回。
局部变量,它是相比于成员变量来说的(或属性)
基本数据类型变量 VS 引用类型变量(类、数组、接口)

虚拟机栈的特点

虚拟机栈的异常

栈中可能出现的异常?
Java 虚拟机规范允许Java栈的大小是动态的或者是固定不变的。

设置栈内存大小

我们可以使用参数 -Xss 选项来设置线程的最大栈空间,栈的大小直接决定了函数调用的最大可达深度。
Sets the thread stack size (in bytes). Append the letter k or K to indicate KB, m or M to indicate MB, and g or G to indicate GB. The default value depends on the platform:

栈的存储单位

栈中存储什么?

栈运行原理

栈帧的内部结构

每个栈帧中存储着:

并行每个线程下的栈都是私有的,因此每个线程都有自己各自的栈,并且每个栈里面都有很多栈帧,栈帧的大小主要由局部变量表 和 操作数栈决定的


image.png

局部变量表

image.png

局部变量表也被称之为局部变量数组或本地变量表

reference类型

reference类型类型表示对一个对象实例的引用。虚拟机规范既没有说明它的长度,也没有明确指出这种引用应有怎样的结构。但一般说来,虚拟机实现至少都是通过这个引用做到两点,一是从此引用中直接或间接地查到对象在Java堆中的数据存放地址索引,二是引用中直接或间接地查找到对象所属数据类型在方法区中的存储的类型信息,否则无法实现Java语言规范中定义的语法约束。
它表示了一个对象实例的引用(堆中),主要的作用有两个:

64位数据类型(long、double)

对于64位的数据类型,虚拟机以高位对齐的方式为其分配两个连续的Slot空间。Java语言中明确的(reference类型则可能是32位也可能是64位)64位的数据类型只有long和double两种。由于局部变量表建立在线程的堆栈上,是线程私有的数据,无论读写两个连续的Slot是否为原子操作,都不会引起数据安全问题。

变量如何在局部变量表中存储的

通过以上描述,我们知道它是以变量槽的方式进行存储。具体流程我们通过下面代码进行分析

public void show() {
        String name = "张三";
        int age = 20;
    }

对应字节码文件

0 ldc #5 <张三>
2 astore_1
3 bipush 20
5 istore_2
6 return

现在分析字节码文件的执行流程

ldc #5 <张三>
将 张三 的值压栈

astore_1
将 张三 弹出栈并将该值的引用赋予变量槽1的位置。因为字符串是在堆中的,需要引用指向

bipush 20
将 20 压入栈

istore_2
将 20 弹出栈,并将该值赋予变量槽2的位置。因为符合变量槽的要求,所以该值在变量槽中

通过以上得出,String是以引用refrence在变量槽中存放的。int 则是值在变量槽中存放的。

为方便理解,画了一个图:

image.png

Slot的访问方式

关于Slot的理解

Slot的重复利用(踩坑点)

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

例:在test4()方法中,局部变量个数为3 —》this,a 和 c ,因为变量b只在其作用域中有效,变量b被销毁,但开辟的空间还在,新定义的变量c就占据了这块已开辟的空间。

image.png

静态变量与局部变量的对比

变量的分类:

按照数据类型分:

按照在类中声明的位置分:

public static void testTemp1(){
    int num1;
    System.out.println(num1); //输出0
}
 
public void testTemp2(){
    int num2;
    System.out.println(num2); //错误信息:变量num未初始化
}
 

类变量表有两次初始化的机会,第一次是在链接中的“准备阶段”,执行系统初始化,对类变量设置零值(final修饰的static不会),另一次则是在“初始化”阶段,赋予程序员在代码中定义的初始值。
和类变量初始化不同的是,局部变量表不存在系统初始化的过程,这意味着一旦定义了局部变量则必须人为的初始化,否则无法使用(编译不会通过)。
在栈帧中,与性能调优关系最为密切的部分就是局部变量表。在方法执行时,虚拟机使用局部变量表完成方法的传递。
局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收。

代码解析

javap -v Demo03.class: 解析class文件

package com.nike.erick.d05;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Demo03 {
    public static void main(String[] args) {

    }

    public Set<String> method01(String address){
        List<String> erickList = new ArrayList<>();
        double phone = 1234567654;
        int age = 100;
        return new HashSet<>();
    }

    public Set<String> method02(String address){
        double phone = 1234567654;

        int a = 1;
        {
            int b = a+1;
            b = 0;
        }
        int age = 100;
        return new HashSet<>();
    }
}

栈帧信息

{
  public com.nike.erick.d05.Demo03();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/nike/erick/d05/Demo03;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  args   [Ljava/lang/String;

  public java.util.Set<java.lang.String> method01(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/util/Set;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=6, args_size=2
         0: new           #2                  // class java/util/ArrayList
         3: dup
         4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
         7: astore_2
         8: ldc2_w        #4                  // double 1.234567654E9d
        11: dstore_3
        12: bipush        100
        14: istore        5
        16: new           #6                  // class java/util/HashSet
        19: dup
        20: invokespecial #7                  // Method java/util/HashSet."<init>":()V
        23: areturn
      LineNumberTable:
        line 14: 0
        line 15: 8
        line 16: 12
        line 17: 16
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      24     0  this   Lcom/nike/erick/d05/Demo03;
            0      24     1 address   Ljava/lang/String;
            8      16     2 erickList   Ljava/util/List;
           12      12     3 phone   D
           16       8     5   age   I
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            8      16     2 erickList   Ljava/util/List<Ljava/lang/String;>;
    Signature: #34                          // (Ljava/lang/String;)Ljava/util/Set<Ljava/lang/String;>;

  public java.util.Set<java.lang.String> method02(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/util/Set;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=6, args_size=2
         0: ldc2_w        #4                  // double 1.234567654E9d
         3: dstore_2
         4: iconst_1
         5: istore        4
         7: iload         4
         9: iconst_1
        10: iadd
        11: istore        5
        13: iconst_0
        14: istore        5
        16: bipush        100
        18: istore        5
        20: new           #6                  // class java/util/HashSet
        23: dup
        24: invokespecial #7                  // Method java/util/HashSet."<init>":()V
        27: areturn
      LineNumberTable:
        line 21: 0
        line 23: 4
        line 25: 7
        line 26: 13
        line 28: 16
        line 29: 20
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           13       3     5     b   I
            0      28     0  this   Lcom/nike/erick/d05/Demo03;
            0      28     1 address   Ljava/lang/String;
            4      24     2 phone   D
            7      21     4     a   I
           20       8     5   age   I
    Signature: #34                          // (Ljava/lang/String;)Ljava/util/Set<Ljava/lang/String;>;
}
SourceFile: "Demo03.java"

上一篇 下一篇

猜你喜欢

热点阅读