内存结构篇:虚拟机栈

一、定义

Java Virtual Machine Stacks (Java 虚拟机栈)(线程独享):

虚拟机栈描述的是Java 方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(StackFrame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

栈 - 线程运行时需要的内存空间

栈帧:包含参数、局部变量、返回地址

  • 每个线程运行时所需要的内存,称为虚拟机栈
  • 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法;栈帧就是每个方法运行时需要的内存空间
  • 生命周期与线程相同
/**
 * 演示栈帧
 */
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;
    }
}

debug:调用方法入栈

方法执行结束出栈:

二、问题辨析

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

不需要,因为方法调用完成后,栈帧会弹出栈,自动回收

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

不是,栈内存越大,可能会导致线程数越少,因为物理内存有限,栈内存越大,可以调用更多的方法,但会导致线程数越少

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

是线程安全的,局部变量是私有的,一个线程中创建的局部变量不会被另一个线程影响,即一个线程不能干扰另一个线程中的栈(除了static修饰的变量,因为 static变量 是线程共享的)

  • 如果方法内局部变量没有逃离方法的作用范围所占用的内存是栈帧中的内存),它是线程安全的
  • 如果是局部变量引用了对象(例如返回了该变量,可能被其他线程调用),并逃离方法的作用范围(栈),需要考虑线程安全

三、栈内存溢出

导致栈内存溢出的情况:

  • 栈帧过多导致栈内存溢出(不停调用方法,没有出栈,出现报错java.lang.StackOverflowError
private static int count;

public static void main(String[] args) {
    try {
        method1();
    } catch (Throwable e) {
        e.printStackTrace();
        System.out.println(count);
    }
}

//方法递归调用导致栈溢出,无法出栈
private static void method1() {
    count++;
    method1();
}
  • 栈帧过大导致栈内存溢出(不太容易出现这种情况)

四、线程运行误诊

定位

  • 用 top 定位哪个进程对cpu的占用过高
  • ps H -eo pid,tid,%cpu | grep 进程id(用ps命令进一步定位是哪个线程引起的cpu占用过高)
  • jstack 进程id
    • 可以根据线程 id (转换成十六进制查看对应的线程)找到有问题的线程,进一步定位到问题代码的源码行号
原文地址:https://www.cnblogs.com/flypig666/p/13975089.html