JVM内存结构 - 栈

本文详细介绍了Java虚拟机栈的定义与工作原理,包括栈帧的构成、内存溢出情况以及线程安全问题。通过实例解析了方法调用过程中的栈帧入栈和出栈顺序,指出垃圾回收不涉及栈内存,而栈内存大小应适中以保证运行效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 栈

        栈是一种“操作受限”的线性表,只涉及在一端插入和删除数据,并且满足后进先出、先进后出的特性。

2 Java虚拟机栈

2.1 定义

        Java 虚拟机栈描述的是 Java 方法执行的内存模型,用于存储栈帧。线程启动时会创建虚拟机栈,每个方法在执行时会在虚拟机栈中创建一个栈帧,用于存储局部变量表、操作数栈、动态连接、方法返回地址、附加信息等信息。每个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中的入栈(压栈)到出栈(弹栈)的过程。

  • 每个线程运行需要的内存空间,称为虚拟机栈;
  • 每个栈由多个栈帧组成,对应着每次方法调用所占用的内存;
  • 栈帧:每个方法运行时需要的内存,一个栈帧表示一个方法的调用;
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法;

2.2 实例

说明:设置一个简单的Demo,主函数调用method1,method1调用method2,我们观察每个方法入栈的顺序及其它们之间的先后顺序。

public class Demo1_1 {

    public static void main(String[] args) throws InterruptedException {
        method1();
    }

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

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

1.程序进入主函数,因此需要先把主函数放入栈顶。

2.主函数调用method1,因此将method1压入栈顶。

3.method1调用method2,将method2压入栈顶

 4.执行完成后,依次从栈顶将栈帧取出,并释放。 

3 栈内存溢出

  • 栈帧过多导致溢出(递归调用未设置结束条件,导致栈帧不断放入栈,无法释放)
  • 栈帧过大导致溢出

4 问题

1. 垃圾回收是否涉及栈内存?

        不需要,每个方法在出栈后自动释放;垃圾回收针对堆内存中的无用对象进行回收。

2. 栈内存分配越大越好吗?

       不是,栈内存过大,会使线程数变少,会影响运行效率,所以正常情况下采用默认值即可。

       不同环境中的默认值:

                 Linux:1024

                macos:1024

                Oracle Solaris: 1024

                Windows:依赖于虚拟内存的默认值

3. 方法内的局部变量是否线程安全?

  • 如果方法局部变量没有逃离方法的作用范围,它是线程安全的;
  • 如果局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全;

实例1:

/**
 * 局部变量的线程安全问题
 */
public class Demo1_18 {

    // 多个线程同时执行此方法,是否会出现X值紊乱的情况
    static void m1() {
        int x = 0;
        for (int i = 0; i < 5000; i++) {
            x++;
        }
        System.out.println(x);
    }
}

答:不会出现紊乱情况

原因:每个线程都有一个单独的虚拟机栈,而且x属于局部变量,隶属于Demo1_18这个方法,运行时以栈帧形式放入栈中,所以在时间运行过程中是在不同栈空间的栈帧里运行的,所以不会影响。

实例2

/**
 * 局部变量的线程安全问题
 */
public class Demo1_17 {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append(4);
        sb.append(5);
        sb.append(6);
        new Thread(()->{
            m2(sb);
        }).start();
    }
    
    //m1是线程安全的
    public static void m1() {      
        StringBuilder sb = new StringBuilder();
        sb.append(1);
        sb.append(2);
        sb.append(3);
        System.out.println(sb.toString());
    }

    //m2不是线程安全的,因为参数对象可能被其他线程引用,造成结果紊乱
    public static void m2(StringBuilder sb) {   
        sb.append(1);
        sb.append(2);
        sb.append(3);
        System.out.println(sb.toString());
    }

    public static StringBuilder m3() {          
        StringBuilder sb = new StringBuilder();
        sb.append(1);
        sb.append(2);
        sb.append(3);
        return sb;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值