Java 虚拟机栈详解分析

作者:小小奶酪可笑可笑 时间:2021-08-29 10:04:55 

Java虚拟机栈

1. 定义

  • 栈:线程运行时需要的内存空间,一个栈存在多个栈帧。栈具有先入后出,后入先出的特点。

  • 栈帧:每个方法运行时需要的内存(局部变量表、操作数栈、动态链接和方法返回值等信息。),每次调用一个方法,便会将栈帧压入栈中,方法执行完毕将栈帧从栈顶压出

  • 活动栈帧:指在栈顶的栈帧,既正在调用的方法,每个线程只能有一个活动栈帧,对应着该线程正在调用的那个方法

现在我们用代码来演示一下Java虚拟机如何将栈帧压入及压出栈中


public class Main {
    public static void main(String[] args) {
        method1();
    }

private static void method1() {
        method2(1, 2);
    }

private static int method2(int a, int b) {
        int c = a + b;
        return c;
    }
}

当我们运行Main函数时,jvm首先将栈帧Main压入栈中,此视栈结构如图所示

Java 虚拟机栈详解分析

Main函数体中调用了method1方法,此时便会将栈帧1压入栈中

Java 虚拟机栈详解分析

method1方法体中调用了method2方法,这时jvm会将栈帧2压入栈结构中,需要注意的是,前面我们提到了栈帧由 “局部变量表、操作数栈、动态链接和方法返回值”等信息组成,method2方法中拥有a,b两个参数以及局部变量c和方法返回值

Java 虚拟机栈详解分析

当method2方法执行完后,会将栈帧2从栈顶弹出

Java 虚拟机栈详解分析

method1方法执行完毕后,将栈帧1弹出

Java 虚拟机栈详解分析

依次顺序直至线程被销毁。

注意点

  • 由于每个栈帧都会在方法调用完毕后被弹出,因此栈内存不需要进行垃圾回收

  • 每个栈都是线程私有的,每个线程在创建的时候都会创建一个虚拟机栈,而由于物理内存是固定的,栈内存划分得越大,可分配的线程数就越少

2. 栈的线程安全问题

局部变量是线程安全的

现在我们定义如下方法:


public static void method() {
    int a = 0;
    a++;
}

我们在方法method中定义了一个局部变量a,并对其执行a++操作,现在假设我们有两个线程同时调用了这个方法(栈帧),Java虚拟机会将该栈帧压入各自线程的栈内存中,但由于局部变量表是线程私有的,所以两个线程在同时调用这个栈帧后,a的值仍然都为1,故局部变量是线程安全的

Java 虚拟机栈详解分析

方法参数和方法返回值不是线程安全

由于方法的参数和返回值均可被外部方法所引用,故在某个线程下某个方法可以更改另外一个方法的参数和返回值,故方法参数和方法返回值不是线程安全的

3. 栈内存溢出

由于程序执行时,虚拟机给每个栈分配的栈内存空间是固定的,所以在一些情况下有可能出现栈内存空间不足,导致溢出的情况,一般有两种情况可能导致栈内存溢出

  • 栈帧过大(较少出现)

  • 栈帧过多(一般出现在递归时。没有正确设置递归出口)

现在我们来解释一下什么时候会出现栈帧过多导致栈内存溢出,我们来假设某一个栈的内存空间大小是1024kb,现在有四个栈帧,每个栈帧的大小均为300kb,而 300 * 4 = 1200kb, 而 1200 > 1024 ,很显然,现在这四个栈帧合起来的大小已经超过了这个栈的内存空间大小,这个时候就会出现栈内存溢出,也就是会报java.lang.StackOverflowError这个错误。

Java 虚拟机栈详解分析

来源:https://blog.csdn.net/qq1104380920/article/details/121199089

标签:Java,虚拟机,栈,jvm
0
投稿

猜你喜欢

  • VSCode配置C语言环境的方法

    2022-11-07 18:47:36
  • C#中判断某类型是否可以进行隐式类型转换

    2023-03-28 15:29:36
  • 解决IDEA maven 项目修改代码不生效,mvn clean、install后才生效

    2022-06-19 12:17:06
  • C#延迟执行方法函数实例讲解

    2022-06-17 19:23:50
  • Java桥梁设计模式优雅地将抽象与实现分离

    2023-12-11 14:56:36
  • Spring Bean生命周期之BeanDefinition的合并过程详解

    2023-11-29 02:50:35
  • 详解SpringBoot自定义配置与整合Druid

    2023-07-24 20:20:28
  • 详解利用spring-security解决CSRF问题

    2023-07-31 14:31:19
  • C#实现Base64编码与解码及规则

    2023-09-17 13:06:19
  • C#抽象类和接口的区别分析

    2023-11-09 13:56:21
  • C#字符串和Acsii码相互转换

    2022-09-24 00:12:07
  • Java中常用的设计模式之工厂模式详解

    2021-07-04 15:33:37
  • java长整除问题浅谈

    2023-05-22 06:06:23
  • 命令行编译java文件方式

    2023-01-18 18:35:47
  • SpringMVC + servlet3.0 文件上传的配置和实现代码

    2023-08-08 16:42:43
  • 详解Java8新特性Stream之list转map及问题解决

    2021-06-12 05:45:06
  • SpringBoot过滤器的使用

    2023-08-28 21:28:56
  • SpringMVC如何接收参数各种场景

    2022-01-23 22:56:24
  • C# MVC 使用LayUI实现下拉框二级联动的功能

    2022-12-07 02:10:05
  • SpringBoot拦截 器如何获取http请求参数

    2023-11-28 19:40:48
  • asp之家 软件编程 m.aspxhome.com