栈的特点
栈(stack)是一种常用的数据结构,遵从先入后出的规则
虚拟机栈与栈帧
当线程运行调用方法时,会给方法划分一段栈帧空间,如方法1划分一个栈帧1,然后压入栈内进行运行,运行完后出栈并释放内存。
当方法1中调用了方法2时,则会划分一个栈帧2的空间压入栈并运行,方法2运行完后返回值交给方法1,栈帧2出栈释放空间,然后运行方法1。
虚拟机栈定义
Java Virtual Machine Stacks ——Java虚拟机栈
- 每个线程运行所需要的内存称为虚拟机栈
- 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
- 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
//演示虚拟机栈运行过程
public class JvmStacks {
public static void main(String[] args) {
method1();
}
public static void method1(){
method2(1,2);
}
static int method2(int a,int b){
int c=a+b;
return c;
}
}
相关问题辨析
1、垃圾回收是否涉及栈内存?
不需要,栈内存只是一次次方法调用产生的栈帧内存,而栈帧内存在运行完后会自动释放。垃圾回收主要是针对堆内存的无用对象。
2、栈内存分配越大越好吗?
分配栈内存指令:
-Xss size
Sets the thread stack size (in bytes). Append the letter k or K to indicate KB, m or M to indicate MB, and g or G to indicate GB. The default value depends on the platform:
Linux/x64 (64-bit): 1024 KB
macOS (64-bit): 1024 KB
Oracle Solaris/x64 (64-bit): 1024 KB
Windows: The default value depends on virtual memory
The following examples set the thread stack size to 1024 KB in different units:
来自https://docs.oracle.com/en/java/javase/11/tools/java.html
-Xss1m
-Xss1024k
-Xss1048576
内存并不是越大越好,物理内存是一定的,当栈的内存大时,就会导致线程数量减少,栈内存大只是可以调用更多的方法,分配更多的栈帧,不会增加你的运行效率。
3、方法内的局部变量是否线程安全?
public class JvmStacks {
public static void main(String[] args) {
StringBuilder sb=new StringBuilder();
sb.append(1);
sb.append(2);
sb.append(3);
//这种创建方法适用于jdk1.8及以上
new Thread(() ->{
method2(sb);
}).start();
}
public static void method1(){
StringBuilder sb=new StringBuilder();
sb.append(1);
sb.append(2);
sb.append(3);
System.out.println(sb.toString());
}
static void method2(StringBuilder sb){
sb.append(4);
sb.append(5);
sb.append(6);
System.out.println(sb.toString());
}
static String method3(){
StringBuilder sb=new StringBuilder();
sb.append(7);
sb.append(8);
sb.append(9);
return sb.toString();
}
}
-
调用方法1时:
调用方法1时两个线程分别产生一个栈帧来存方法1,同时新建一个sb,互不影响。 -
调用方法2:
调用方法2时有一个main方法中的全局变量sb,线程1和线程2都得读取这个sb并写回sb。 -
调用方法3
调用方法3时,虽然没引用对象,但有返回值,可能有别的方法获得这个返回值,所以不是线程安全的;
所以:
- 如果方法内局部变量没有逃离方法的作用范围,它是线程安全的
- 如果是局部变量引用了对象,并逃离方法的作用的作用范围,需要考虑线程安全