1. 什么是虚拟机栈
虚拟机栈是 Java虚拟机(JVM) 运行时数据区的一部分,是线程私有的内存区域,每个线程在创建时都会创建一个虚拟机栈。
简单来说就是:每个线程运行时所需要的内存,称为虚拟机栈,先进后出。
主要特点:
- 线程私有:每个线程都有自己独立的虚拟机栈
- 生命周期:与线程相同,随线程创建而创建,随线程结束而销毁
- 存储内容:存储栈帧(Stack Frame),即方法调用的数据结构
- 异常:可能抛出StackOverflowError(栈深度超过限制)和OutOfMemoryError(无法扩展栈时)
2. 栈帧
栈帧(frame)是虚拟机栈的基本组成单元,是用于支持 Java 方法调用和执行的数据结构。
每次方法调用时,JVM都会创建一个新的栈帧并压入虚拟机栈;
方法执行结束时,对应的栈帧会被弹出。
栈帧 和 栈 的关系:
整体与部分的关系:
- 虚拟机栈是由多个栈帧组成的后进先出(LIFO)的数据结构
- 每个栈帧对应一个方法调用(每个线程(栈)只能有一个活动栈帧,对应着当前正在执行的那个方法)
生命周期关系
线程启动时创建虚拟机栈
方法调用时创建栈帧并压入栈
方法结束时弹出栈帧
线程结束时销毁虚拟机栈
3. 常见问题
3.1 垃圾回收是否涉及栈内存?
垃圾回收主要指的就是堆内存,当栈帧弹出之后,内存就会释放。
3.2 栈内存分配越大越好吗?
未必,默认的栈内存通常为 1024k
栈帧过大会导致线程数变少,例如,机器总内存为 512m,目前能活动的线程数则为 512 个,如果把栈内存改为 2048k,那么能活动的栈帧就会减半。
3.3 栈内的局部变量是否线程安全?
- 如果方法内局部变量没有逃离方法的作用范围,它是线程安全的
- 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全
3.4 栈内存溢出的情况
- 栈帧过多导致栈内存溢出,典型问题:递归调用
- 栈帧过大导致栈内存溢出(不太常见)
3.5 堆和栈的区别
- 栈内存一般会用来存储局部变量和方法调用,但堆内存是用来存储Java对象和数组的的。堆会GC垃圾回收,而栈不会。
- 栈内存是线程私有的,而堆内存是线程共有的。
- 两者异常错误不同,但如果栈内存或者堆内存不足都会抛出异常。栈空间不足:java.lang.StackOverFlowError。堆空间不足:java.lang.OutOfMemoryError.
特性 | 堆(Heap) | 栈(Stack) |
---|---|---|
存储内容 | 对象实例 | 基本类型变量、对象引用 |
线程共享 | 是 | 每个线程私有 |
内存分配 | 动态 | 固定大小(可通过参数调整) |
垃圾回收 | 是 | 否(随线程结束自动释放) |
空间大小 | 较大 | 较小 |
分配效率 | 相对较慢 | 相对较快 |