java虚拟机在运行java程序中管理的内存叫做运行时数据区,运行时数据区分为虚拟机栈,本地方法栈,程序计数器,堆,方法区。如图
一. 程序计数器(线程私有)
程序计数器是用来存储字节码指令地址的,虚拟机在将字节码指令读取到内存中之后,会将源文件中的偏移量转化为内存地址。每个字节码指令都有一个内存地址。在代码执行过程中,程序计数器会记录下一行字节码指令的地址。执行完当前指令后,虚拟机执行引擎会根据程序计数器执行下一行指令。
程序计数器不存在内存溢出问题,因为它本身只存储一个固定长度的内存地址,程序计数器是线程不共享的。所以他不会发生内存溢出。
二. java虚拟机栈(线程私有)
java虚拟机栈使用栈的数据结构来管理方法调用中的的基本数据,先进后出,每一个方法的调用使用一个栈帧来保存,虚拟机栈跟生命周期更线程是一样的。
栈帧由局部变量表,操作数栈和帧数据组成。
1.局部变量表(线程私有)
存放运行时的一些局部变量,本质是类似一个数组,每一个局部变量对应一个下标,和若干个卡槽,不同类型的局部变量分配的卡槽数量不同int是一个,long是两个。还有this关键字表示当前的实例对象会存储在局部变量表,方法中的参数也会保存在局部变量表中。
局部变量表中的槽是可以复用的,一旦某个变量不在生效时,这个槽可以重新使用。
2.操作数栈(线程私有)
存放一些临时需要使用的数据,比如我们定义一个int类型的变量值为1,字节码指令先将他放入操作数栈中,然后再将他转移到局部变量表中。
3.帧数据
3.1当前字节码指令引用了其他类的属性和方法时,需要将符号引用(编号)转化为常量池中的内存地址,动态链接保存了编号和常量池内存地址的映射关系。
3.2当方法正确执行或者出现异常时都会将当前栈帧弹出,同时返回上一个栈帧中的第一条指令地址,所以帧数据应该存储上一个栈帧的第一条指令地址,这个就叫方法出口。
3.2当出现异常并捕获到时,会生成一个异常表里面存放异常捕获生效的范围和方式异常后跳转的第一条指令。如图
三.本地方法栈
跟虚拟机栈类似,只不过这里的方法时用c++编写的本地方法。所以,本地方法栈和虚拟机栈都是同一个栈。
四.堆(线程公有)
一般java程序中堆内存时最大的一块内存区域。创建出来的对象都放在堆中。栈中的局部变量可以存放堆上的对象引用。静态变量也可以存放堆对象引用,通过静态变量可以实现对象再线程中的共享。
堆内存是会发生内存泄漏的当创建的对象大于jvm所分配的最大内存时会溢出。
五.方法区(线程公有)
方法区时存放基础信息的位置,主要包括三个内容:所有类的基本信息,运行时的常量池,字符串常量池。
方法区是用来存储每个类的基本信息(元信息)一般称为instaanceKlass对象,再类的加载阶段完成。在jdk7中,方法区是存放在堆区域的永久代中的。jdk8后,将方法区存放在原空间中,原空间是位于直接内存的
当方法区存放的内容超过元空间大小时,方法区也会发生内存溢出。
字符串常量池在jdk6实在方法区,也就是永久代,jdk7,字符串常量池被移到堆区域中。jdk8后,将方法区存放在原空间中,字符串常量池还在堆中。