每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。
可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小,在 JDK 1.4 中默认为 256K,而在 JDK 1.5+ 默认为 1M:
java -Xss2M HackTheJava
该区域可能抛出以下异常:
- 当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常;
- 栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。
局部变量表
- 定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型、对象引用(reference),以及returnAddress类型。
- 由于局部变量表是建立在线程的栈上,是线程的私有数据,因此不存在数据安全问题
局部变量表所需的容量大小是在编译期确定下来的,并保存在方法的Code属性的maximum local variables数据项中。在方法运行期间是不会改变局部变量表的大小的。
关于Slot的理解
1.参数值的存放总是从局部变量数组索引 0 的位置开始,到数组长度-1的索引结束。
2.局部变量表,最基本的存储单元是Slot(变量槽),局部变量表中存放编译期可知的各种基本数据类型(8种),引用类型(reference),returnAddress类型的变量。
3.在局部变量表里,32位以内的类型只占用一个slot(包括returnAddress类型),64位的类型占用两个slot(1ong和double)。- byte、short、char在储存前被转换为int,boolean也被转换为int,0表示false,非0表示true
- long和double则占据两个slot
4.当一个实例方法被调用的时候,它的方法参数和方法体内部定义的局部变量将会按照顺序被复制到局部变量表中的每一个slot上
5.如果当前帧是由构造方法或者实例方法创建的,那么该对象引用this将会存放在index为0的slot处,其余的参数按照参数表顺序继续排列。(this也相当于一个变量)
public String test2(Date dateP, String name2) {dateP = null;name2 = "songhongkang";double weight = 130.5;//占据两个slotchar gender = '男';return dateP + name2;}








栈相关面试题
举例栈溢出的情况?
SOF(StackOverflowError),栈大小分为固定的,和动态变化。如果是固定的就可能出现StackOverflowError。如果是动态变化的,内存不足时就可能出现OOM
调整栈大小,就能保证不出现溢出么?
分配的栈内存越大越好么?
不是,一定时间内降低了OOM概率,但是会挤占其它的线程空间,因为整个虚拟机的内存空间是有限的
垃圾回收是否涉及到虚拟机栈?
不会
| 位置 | 是否有Error | 是否存在GC |
|---|---|---|
| PC计数器 | 无 | 不存在 |
| 虚拟机栈 | 有,SOF | 不存在 |
| 本地方法栈(在HotSpot的实现中和虚拟机栈一样) | ||
| 堆 | 有,OOM | 存在 |
| 方法区 | 有 | 存在 |
方法中定义的局部变量是否线程安全?
具体问题具体分析
- 如果只有一个线程才可以操作此数据,则必是线程安全的。
- 如果有多个线程操作此数据,则此数据是共享数据。如果不考虑同步机制的话,会存在线程安全问题。
具体问题具体分析:
如果对象是在内部产生,并在内部消亡,没有返回到外部,那么它就是线程安全的,反之则是线程不安全的。 ```java /**
- 面试题:
- 方法中定义的局部变量是否线程安全?具体情况具体分析 *
- 何为线程安全?
- 如果只有一个线程才可以操作此数据,则必是线程安全的。
如果有多个线程操作此数据,则此数据是共享数据。如果不考虑同步机制的话,会存在线程安全问题。 */ public class StringBuilderTest {
int num = 10;
//s1的声明方式是线程安全的(只在方法内部用了) public static void method1(){ //StringBuilder:线程不安全 StringBuilder s1 = new StringBuilder(); s1.append(“a”); s1.append(“b”); //… } //sBuilder的操作过程:是线程不安全的(作为参数传进来,可能被其它线程操作) public static void method2(StringBuilder sBuilder){ sBuilder.append(“a”); sBuilder.append(“b”); //… } //s1的操作:是线程不安全的(有返回值,可能被其它线程操作) public static StringBuilder method3(){ StringBuilder s1 = new StringBuilder(); s1.append(“a”); s1.append(“b”); return s1; } //s1的操作:是线程安全的(s1自己消亡了,最后返回的只是s1.toString的一个新对象) public static String method4(){ StringBuilder s1 = new StringBuilder(); s1.append(“a”); s1.append(“b”); return s1.toString(); }
public static void main(String[] args) { StringBuilder s = new StringBuilder();
new Thread(() -> {s.append("a");s.append("b");}).start();method2(s);}
} ```
