Java工程师知识树

Java基础-JVM内存管理-虚拟机栈

2021-03-04  本文已影响0人  HughJin

Java工程师知识树 / Java基础


虚拟机栈特点

虚拟机栈出现的背景:

内存中的栈与堆:

栈是运行时的单位,堆是存储的单位。即栈解决程序运行的问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题。

虚拟机栈的基本内容

一句话概述虚拟机栈:
Java虚拟机栈主管Java程序的运行,它保存方法的局部变量、部分结果,并参与方法的调用和返回。Java虚拟机栈保存的局部变量只是八种基础数据类型,Java虚拟机栈中还保存引用数据类型(类、数组、接口)的地址。

栈的特点:

虚拟机栈配置

JVM提供了-Xss来指定线程的最大栈空间, 该参数也直接决定了函数调用的最大深度.

测试代码:

package com.jvm.study;
public class StackTest2 {
    private static int i = 0;
    private static void foo() {
        i++;
        foo();
    }
    public static void main(String[] args) {
        try {
            foo();
        } catch (Throwable t) {
            System.out.println(i);
        }
    }
}

配置VM options:

-Xss128M对应执行结果:

8344641

-Xss1M对应执行结果:

23096

默认配置对应执行结果:

23359

栈帧存储数据类型

栈帧内部“数据结构”主要由这几个部分组成:局部变量表、操作数栈、动态链接、方法出口等信息。

存放基本数据类型变量、引用类型的变量、returnAddress类型的变量

在执行字节码指令过程中被用到,这种方式类似于原生 CPU 寄存器。
大部分 JVM 字节码把时间花费在操作数栈的操作上:入栈、出栈、复制、交换、产生消费变量的操作。
因此,局部变量数组和操作数栈之间的交换变量指令操作通过字节码频繁执行。
比如,一个简单的变量初始化语句将产生两条跟操作数栈交互的字节码。

每个栈帧都有一个运行时常量池的引用。这个引用指向栈帧当前运行方法所在类的常量池(保存在方法区)。通过这个引用支持动态链接(dynamic linking)。

当 Java 类文件编译时,所有变量和方法的引用都被当做符号引用存储在这个类的常量池中。
符号引用是一个逻辑引用,实际上并不指向物理内存地址。
JVM 可以选择符号引用解析的时机,一种是当类文件加载并校验通过后,这种解析方式被称为饥饿方式。另外一种是符号引用在第一次使用的时候被解析,这种解析方式称为惰性方式。无论如何 ,JVM 必须要在第一次使用符号引用时完成解析并抛出可能发生的解析错误。
绑定是将对象域、方法、类的符号引用替换为直接引用的过程。绑定只会发生一次。一旦绑定,符号引用会被完全替换。如果一个类的符号引用还没有被解析,那么就会载入这个类。
每个直接引用都被存储为相对于存储结构(与运行时变量或方法的位置相关联的)偏移量。

测试执行中栈中操作代码

public class StackTest2 {
    public static void foo() {
        int a = 1;
        int b = 2;
        int c = (a + b) * 5;
    }
    public static void main(String[] args) {
        foo();
    }
}

反编译文件

public class StackTest2 {
  public StackTest2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void foo();
    Code:
       0: iconst_1
       1: istore_0
       2: iconst_2
       3: istore_1
       4: iload_0
       5: iload_1
       6: iadd
       7: iconst_5
       8: imul
       9: istore_2
      10: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #2                  // Method foo:()V
       3: return
}

16进制查看class文件为

main方法执行后,基于栈的Hotspot的执行过程如下:

注:求值栈就是操作数栈

基于寄存器的DalvikVM执行过程如下所示:

上述两种方式最终通过JVM执行引擎,CPU接收到的汇编指令是:

栈运行原理

示例程序

package com.jvm.study;

public class StackFrameTest {
    public static void main(String[] args) {
        StackFrameTest frameTest = new StackFrameTest();
        frameTest.method1();
    }

    public void method1() {
        System.out.println("Method1开始执行。。。");
        method2();
        System.out.println("Method1结束执行。。。");
    }

    public void method2() {
        System.out.println("Method2开始执行。。。");
        method3();
        System.out.println("Method2结束执行。。。");
    }

    public void method3() {
        System.out.println("Method3开始执行。。。");
        System.out.println("Method3结束执行。。。");
    }
}
执行结果:
Method1开始执行。。。
Method2开始执行。。。
Method3开始执行。。。
Method3结束执行。。。
Method2结束执行。。。
Method1结束执行。。。

以上示例程序对应的栈的示意图:

上一篇 下一篇

猜你喜欢

热点阅读