Java内存区域——Java虚拟机栈

 

Java内存区域——Java虚拟机栈_第1张图片

Java虚拟机栈这块区域的功能,描述的是Java方法执行动态内存模型。就是说,一个方法的执行以及执行完毕,那么,整个的内存模型就是在Java虚拟机栈中。

每个方法执行都会创建一个栈帧,伴随着方法从创建到执行完成。栈帧是用于存储方法可执行的局部变量表、操作数栈、动态链接,方法出口等。每次方法执行,栈帧都会伴随着一个进栈以及出栈的这么一个过程。我们可以简单的描述一下这个过程,这是我们的Java虚拟机栈

Java内存区域——Java虚拟机栈_第2张图片

栈内存是一端开口,先进后出的原则,每一个方法要执行,首先要创建一个栈帧

Java内存区域——Java虚拟机栈_第3张图片

每一个方法执行都要创建一个栈帧,那么,这个栈帧里面所存储的就是局部变量表、操作数栈、动态链接、方法出口等,我们创建好了栈帧之后,方法就开始进栈,进栈就开始执行,那么,在执行的过程中,比如说它调用了另外的方法,那么,另外这个方法也需要创建一个栈帧,接着,它所调用的那个方法进栈

Java内存区域——Java虚拟机栈_第4张图片

那么,这个方法就开始执行了,那么,这个方法执行到方法出口之后,这个方法就执行完毕了,方法执行完毕之后,这个栈帧就出栈了,出栈之后就销毁了

Java内存区域——Java虚拟机栈_第5张图片

然后接着执行它

Java内存区域——Java虚拟机栈_第6张图片

那么,这个方法继续执行,一直执行完毕,出栈

Java内存区域——Java虚拟机栈_第7张图片

这就是一个方法执行的过程。也就是说,Java虚拟机栈描述的就是Java方法执行的动态内存模型。

什么是局部变量表呢?

局部变量表所需的内存空间其实是在编译期就完成了分配,当进入到一个方法的时候,这个方法需要在栈中分配多少内存其实是固定了的,那么在方法运行期间,这个内存大小是不会改变的。

Java内存区域——Java虚拟机栈_第8张图片

大家这里可能有疑问了,比如引用类型,比如我们这里有一个User对象,这个对象里面有一个name属性,那么,我们在运行期间可能给这个name指定值

Java内存区域——Java虚拟机栈_第9张图片

这个name属性是String类型

Java内存区域——Java虚拟机栈_第10张图片

我们可能会给name这个属性指定值,那么,name属性值的长度肯定是不固定的,那么这个时候,它的内存区域是不会变化的吗?为什么在方法运行期间是不会改变局部变量表的大小?其实,我们在局部变量表中所引用的只是一个对象的引用,后面会讲,对象的创建会创建到堆内存中,而局部变量表所存储的是一个对象的引用,那么,这个对象的引用,这个大小是不会改变的,所以,在方法运行期间局部变量表的大小是不会改变的

接下来我们说一下Java虚拟机栈的大小的问题。

Java内存区域——Java虚拟机栈_第11张图片

如果虚拟机栈放不下栈帧了怎么办?即虚拟机栈中放满了栈,但是依然去调用方法,这个时候,我们就遇到了我们所熟悉的StackOverFlowError,这叫栈内存溢出,这个异常其实是我们如果写一个递归调用的方法,就非常常见这个异常。我们来模拟一下栈内存溢出,我们知道每一个方法的执行都会进栈

Java内存区域——Java虚拟机栈_第12张图片

Java内存区域——Java虚拟机栈_第13张图片

我们通过内存模型来分析一下这个报错的原因,首先有一个Java虚拟机栈

Java内存区域——Java虚拟机栈_第14张图片

首先

Java内存区域——Java虚拟机栈_第15张图片

调用tes()方法,tes()方法进栈

Java内存区域——Java虚拟机栈_第16张图片

这个方法进栈之后,首先执行第六行代码,然后接着执行第七行代码,执行第七行代码的时候,Java虚拟机发现又执行了一个方法,接着这个方法又进栈,所以,不停的进栈,方法之间不停的调用,不停的进栈,那么,我们就会发现

Java内存区域——Java虚拟机栈_第17张图片

栈内存就会出现溢出的问题,那么,也就导致了StackOverflowError这个异常的抛出。如果我们把这个栈的大小改成非常大,就是,我们不限定这个栈的深度的大小,那么,这个时候它会不停的去申请,如果不限定栈大小的话,如果当栈的内存区域已经大于Java虚拟机设定的内存,或者说,已经超出了我们机器本身的内存,已经申请不下来内存的时候,就会抛出OutofmemeryError,就是内存溢出。

Java内存区域——Java虚拟机栈_第18张图片

 

你可能感兴趣的:(JVM)