JVM之自动内存管理
Java虚拟机运行时数据区有一些区域是全局共享的,随着虚拟机启动而创建,随着虚拟机退出而销毁。有一些区域是线程私有的,随着线程开始和结束而创建和销毁。
Java虚拟机运行时数据区的划分:
- 程序计数器
- Java堆
- Java虚拟机栈
- 本地方法栈
- 方法区
程序计数器
- 一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。
- 如果线程正在执行Java方法,这个计数器的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地Native方法,这个计数器值则为空。
- 此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
Java虚拟机栈
Java虚拟机栈描述的是Java方法执行时候的内存概念模型,每个方法在执行的时候,都会创建一个栈帧用来创建这个方法的操作数栈、局部变量表、方法出口、动态链接等信息,每一个方法在调用或结束过程就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
Java方法的调用、执行和退出都与Java虚拟机栈中存储的栈帧有着密切的联系。
在虚拟机规范之中为虚拟机栈规定了两种异常,如果线程请求的栈深度大于JVM所允许的最大深度将会抛出StackOverflowError异常;如果Java虚拟机栈被设计成可以动态扩展的,而动态扩展又无法申请到足够的内存将会抛出OutOfMemoryError异常。
Java本地方法栈
与Java虚拟机栈非常相似的,它们之间的差别不过是,Java虚拟机栈为执行了JVM执行Java字节码服务的,而本地方法栈则是为了JVM执行native方法所服务的,因此Java本地方法栈也是一个私有的内存区域。
作用是支撑native方法的调用、执行和退出。
Java虚拟机规范规定了可能出现OutOfMemoryError和StackOverflowError异常。
Java虚拟机规范并没有对本地方法栈所使用的语言,方法的数据结构,方法的形式做任何详细的规定。因此任何一款具体Java虚拟机实现都可以以自己的方式来实现本地方法栈,甚至有些虚拟机比如我们经常使用的HotSpot虚拟机将Java虚拟机栈和本地方法栈合二为一了。
栈帧
栈帧被设计为用于存储数据和部分过程结果的数据结构。同时也被用来处理动态链接、方法返回值和异常分派。
一个完整的栈帧包含:局部变量表、操作数栈、动态链接、方法返回地址和异常信息。
在编译程序代码的时候,栈帧需要多大的局部变量表,需要多深的操作数栈,在编译期已经完全确定了。JAVA编译器会把这些信息写入到class文件code表中,因此一个栈帧需要分配多大的内存空间,是不会受程序运行期间变量数据的影响,而仅仅取决于具体JVM。
在一个线程里面,方法调用的调用链可能会很长,很多方法同时处于执行状态,对于执行引擎而言,在活动线程之中,只有位于JVM栈顶那个栈帧才是有效的,这个栈帧被称为当前栈帧,与这个栈帧相关联的方法被称为当前方法,虚拟机执行引擎中,所有执行的字节码指令,都针对当前栈帧当前方法进行操作。
局部变量表
是一组变量值的存储空间,它用于存储方法和参数以及方法内部定义的局部变量等等,在Java编译器编译class文件的时候,就在该方法的code属性确定了该方法所需要局部变量表最大容量,局部变量表的容量是以变量曹(slot)为最小单位,JVM规范之中并没有指定一个slot所占用的内存空间大小。
单个slot可以存储一个类型为boolean、byte、char、short、float、reference和return Address(不再使用了)的数据,两slot可以存储一个类型为long或double的数据。
reference:JVM至少能通过reference完成两件事情,第一,直接或者间接查找到这个Java对象存放在Java堆中实例数据地址;第二,直接或者间接查找到这个对象所属的数据类型在Java方法区之中的类型信息。