Java栈是我们最常用的内存区域,线程私有。它主要用来存放基本类型变量、局部变量以及对象的引用。例如:User user = new User(),这里的 user 就是对象对象的引用也可以理解为地址,指引着虚拟机要去哪里找 user 这个对象。它们的基本关系如图:
由图可知,当我们将一个对象作为方法的参数时,我们在方法中改变对象的值,也会影响到原来的对象的值,因为我们只是改变了图中内存区域的值,它们的指引(地址)还是一样的。同时也可以看出,栈的内存区域是连续的,有大小限制的,如果超出了就会抛出栈溢出的错误 StackOverflowError【属于Error,程序会终止,无法捕获处理】 。
在每个方法执行的时候,都会在栈区域创建一个个的栈帧,用于保存局部变量表、操作数栈、动态链接等信息。每次方法的调用都会对应着一个栈帧,因此可以解释我们在写递归程序的时候会不小心报栈溢出的错误:因为栈是有限的,方法调用太多次就会导致栈帧堆满了栈,所以溢出。
代码演示:
package org.zpli.java8.jvmapi;
/**
* @Description: TODO
* @Copyright:
* @author:
* @Date: 2020/3/26 17:18
*/
public class StackOverflowErrorDemo {
private int stackCount;
public static void main(String[] args) {
StackOverflowErrorDemo demo = new StackOverflowErrorDemo();
try {
demo.stackOverflow();
} catch (Throwable e) {
System.out.println("stackCount : " + demo.stackCount);
e.printStackTrace();
}
}
private void stackOverflow() {
stackCount++;
stackOverflow();
}
}
输出结果:
stackCount : 20738
java.lang.StackOverflowError
at org.zpli.java8.jvmapi.StackOverflowErrorDemo.stackOverflow(StackOverflowErrorDemo.java:25)
at org.zpli.java8.jvmapi.StackOverflowErrorDemo.stackOverflow(StackOverflowErrorDemo.java:25)
at org.zpli.java8.jvmapi.StackOverflowErrorDemo.stackOverflow(StackOverflowErrorDemo.java:25)
at org.zpli.java8.jvmapi.StackOverflowErrorDemo.stackOverflow(StackOverflowErrorDemo.java:25)
at org.zpli.java8.jvmapi.StackOverflowErrorDemo.stackOverflow(StackOverflowErrorDemo.java:25)
每次在方法执行的时候会进行入栈操作,在方法执行完毕以后会进行出栈操作,虚拟机会自动释放掉为该栈所分配的空间。栈结构采用先进后出的操作方式,先入栈的数据总是在栈底,后入栈的数据总是在栈顶,出栈的时候总是从栈顶开始。
总结:
- 栈是线程私有的
- 存放基本类型变量,局部变量,对象的引用;
- 系统自动分配与回收内存,效率较高,快速,存取速度比堆要快;
- 是一块连续的内存的区域,有大小限制,如果超过了就会栈溢出,并抛出栈溢出的异常StackOverflowError;
- 方法执行完毕以后 jvm 会自动释放掉为该变量所分配的内存空间;
栈又分为java栈和本地方法栈。顾名思义,本地方法栈自然就是为本地方法提供服务的,java栈是为java服务的。