前言:
(栈并不是越大越好,越多可以防止出现StackOverflowError晚点出现,但是栈越大,也就代表着虚拟机栈是一定的,你的栈越大,别的栈就会小)
①. 什么是Java virtual machine?
- ①. 栈的概述
每创建一个线程就会创建一个Java栈,每一个Java栈中都会有很多栈帧(局部变量表 | 操作数栈 | 动态链接 | 方法返回地址 | 一些附加信息)
注意:虚拟机栈中不存在GC,但是存在StackOverflowError和OOM
解释:
(1). 虚拟机栈(Java Virtual Machine Stacks)和线程是紧密联系的,每创建一个线程时就会对应创建一个Java栈,所以Java栈也是"线程私有"的内存区域,这个栈中又会对应包含多个栈帧,每调用一个方法时就会往栈中创建并压入一个栈帧,栈帧是用来存储方法数据和部分过程结果的数据结构,每一个方法从调用到最终返回结果的过程,就对应一个栈帧从入栈到出栈的过程[先进后出]
(2). 栈帧中有如下部分组成:
(3). 栈的其他介绍
- ②. 存放于栈中的东西如下
(8种基本类型的变量+对象的引用变量+实例方法都是在函数的栈内存中分配[局部变量])
- ③. 栈内存溢出(StackOverflowError)
-Xss
参数
- 栈帧过多导致栈内存溢出(方法的递归调用,没设置正确停止条件)
- 局部数组过大。当函数内部的数组过大时,有可能导致堆栈溢出
Exception in thread "main" java.lang.StackOverflowError //sayHello()发生了递归 public class DemoT { public static void main(String[] args) { sayHello(); } public static void sayHello(){ sayHello(); } }
④. Java虚拟机规范允许Java栈的大小是动态的或者是固定不变的 掌握
如果采用固定大小的Java虚拟机栈,那每一个线程的Java虚拟机栈容量可以在线程创建的时候独立选定。
如果线程请求分配的栈容量超过Java虚拟机栈允许的最大容量,Java虚拟机将会抛出一个StackoverflowError异常
如果Java虚拟机栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者是在创建新的线程时就没有足够的内存区创建对应的虚拟机栈,那Java虚拟机将会抛出一个OutOfMemoryError异常
⑤. 如何设置栈内存的大小? -Xss size (即:-XX:ThreadStackSize)
一般默认为512k-1024k,取决于操作系统(jdk5之前,默认栈大小是256k;jdk5之后,默认栈大小是1024k)
栈的大小直接决定了函数调用的最大可达深度
平台 | jdk8 |
linux | 1024 |
MacOS | 1024 |
window | 0 |
⑥. 栈和堆的区别是什么?
从GC、OOM、StackOverflowError的角度。
(栈中不存在GC,当固定大小的栈会发生StackOverflowError,动态的会发生OOM。堆中GC、OOM、StackOverflowError都存在)
从堆栈的执行效率
(栈的效率高于堆)
内存大小,数据结构
(堆的空间比栈的大一般,栈是一种FIFO先进后出的模型。堆中结构复杂,可以有链表、数组等)
栈管运行,堆管存储
②. 局部变量表(LocalVariables)
①. 定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量(这些数据类型包括各种基本数据类型、对象引用(reference)以及return Address类型)
②. 由于局部变量是建立在线程的栈上,是线程私有数据,因此不存在数据安全问题
③. 局部变量表所需容量大小是在编译期确定下来的。(并保存在方法Code属性的maximum local variables数据项中,在方法运行期间不会改变局部变量表的大小的)
//使用javap -v 类.class 或者使用jclasslib public class LocalVariableTest { public static void main(String[] args) { LocalVariableTest test=new LocalVariableTest(); int num=10; test.test1(); } public static void test1(){ Date date=new Date(); String name="xiaozhi"; } }
jclasslib说明:
④. 关于slot的理解(引用数据类型(方法的返回地址)占用1个slot)
局部变量表,是基本的存储单元是slot(变量槽)
在局部变量表中,32位以内的类型只占有一个slot(包括引用数据类型),64位的类型(long和double)占有两个slot
byte、short、char在存储前被转换为int,boolean也被转换为int(0表示fasle,非0表示true)。long和double则占据两个slot
- ⑤. Jvm会为局部变量表中的每一个slot都分配一个访问索引,通过这个索引即可成功访问到局部变量表中指定的局部变量值
- ⑥. 如果需要访问局部变量表中一个64bit的局部变量值时,只需要使用前一个索引即可(比如:访问long或double类型变量)
⑦. 如果当前帧是由构造方法或者实例方法创建,那么该对象引用this将会放在index为0的slot处
⑧. 栈帧中的局部变量表中的槽位是可以复用的,如果一个局部变量过了其作用域,那么在其作用域之后申请的新的局部变量就很可能会复用过期局部变量的槽位,从而节省资源的目的
public void localVar2(){ { int a=0; System.out.println(a); } //此时的b就会复用a的槽位 int b=0; }
⑨. 与GC Roots的关系
局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收