Java虚拟机栈(Java Virtual Machine Stack)是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型;每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表,操作数栈,动态连接和方法返回地址等信息,每个方法被调用直至执行完毕的过程,会对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表存放了编译期可知的各种Java虚拟机基本数据类型(boolean(only ture or false),[byte(1),short(2),char(2),int(4),long(8),float(8),double(8),按精度由低至高]),对象引用(reference类型,它并不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄(对象变量,如String s中的s可以视为一个句柄)或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。
Java中当把精度低的数据类型变量的值赋值给精确度高的变量时,系统自动完成数据类型的转换。
如float x = 100;(将int类型数据值100赋值给精确度更高的float类型变量x时,系统会自动完成数据类型转换)。
Java中当把精确度高度的数据类型变量的值赋值给精确度低的变量时,必须使用类型转换运算。
如int x = (int) 3.141592653;
这些数据类型在局部变量表中的存储空间以局部变量槽(Slot)来表示,其中64位长度的long和double类型的数据会占用两个变量槽,其余的数据类型只会占用一个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间时完全确定的,在方法运行期间不会改变局部变量表所用空间的大小。当然这里所说的“大小”是指变量槽的数量,虚拟机真正使用多大的内存空间(如按照一个变量槽占用32个比特或者64个比特)来实现一个变量槽,这是完全具体的虚拟机实现自行决定的事情。
在《Java虚拟机规范》中,对这个内存区域规定了两类异常状况,如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。HotSpot虚拟机的栈容量时不可以动态扩展的,以前的Classic虚拟机是支持栈的动态扩展。所以在HotSpot虚拟机上是不会由于虚拟机栈无法扩展而导致OutOfMemoryError异常——只要线程申请空间成功了就不会出现OutOfMemoryError异常,但是如果申请时就失败,仍然会出现OutOfMemoryError的异常。
参考书:深入理解Java虚拟机——JVM高级特性与最佳实践