1,程序计数器
程序计数器是一块较小的内存空间,可以看作是当前线程执行的字节码的行号指示器,通过改变指示器的值来选取下一条执行的字节码指令,帮助程序完成执行过程中存在的分支,循环,跳转,异常,线程恢复等功能。担任线程助跑器的职责,所以为线程私有内存区,同时是运行时数据区中唯一不会出现OutOfMemoryError的区域。
2,虚拟机栈
虚拟机栈描述的是Java方法(也就是字节码)执行的内存模型,每个方法在执行的同时都会创建一个栈帧,用于存放局部变量表,操作数栈,动态链接,方法出口等信息。一个方法的调用到完成对应一个栈帧在虚拟机栈中的入栈到出栈,虚拟机栈的生命周期与线程相同,属于线程私有内存区,会抛出stackoverflowerror和OutOfMemoryError异常。
栈帧:用于支持虚拟机方法调用和方法执行的数据结构。存放局部变量表,操作数栈,动态链接,方法出口等信息。
局部变量表:存放调用方法是传入的参数和方法内定义的局部变量。编译时即可确定局部变量表的大小。
操作数栈:代码执行指令指向某一行时,把数据放到操作数栈中,先进后出。有时为了节省参数复制的时间空间,会共享下面栈帧的部分操作数栈和上面栈帧的部分局部变量表。
动态链接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,为了支持方法调用过程中把常量池中方法的符号引用转化为直接引用。
方法返回地址:方法退出时,都要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来恢复上层方法的执行状态,但当异常退出时并没有保存这些信息。方法退出的过程即是把当前栈帧出栈,即恢复上层方法的局部变量表和操作数栈,把返回值压入调用者栈帧的操作数栈中,调整pc计数器的值以指向方法调用指令的下一条指令。
3,本地方法栈
本地方法栈为虚拟机执行native方法服务,生命周期也与线程关联,属于线程私有内存区,会抛出stackoverflowerror和OutOfMemoryError异常。
4,堆内存
堆内存的唯一作用就是存放对象实例,这是一块被所有线程共享的内存,在虚拟机启动时创建,根据启动参数制定不同分区大小,会抛出OutOfMemoryError异常。
对象实例包括3块:对象头,实例数据,对齐填充。
头像头:存放对象运行时数据,包括哈希码,gc分代年龄(即在两个from survivor来回复制的次数),锁状态标志,线程持有的锁,偏向线程id,偏向时间戳,还可能存放对象的类型指针,这取决于虚拟机实现的对象访问方式,使用直接指针时类型指针放在对象头中,但使用句柄时类型指针跟实例指针都放在句柄中,中间多了一层。如果对象是一个数组,还要记录数组的长度。
句柄访问
直接指针访问
实例数据:保存定义的各种类型的字段内容,包括从父类继承的数据。
对齐填充:充当占位符的角色,没有实际意义,也并非必须存在,只是由于jvm自动内存管理系统要求对象起始地址必须是8字节(B)的整数倍(64bit的整数倍)。
5,方法区
方法区用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。另外编译后的class文件的类信息中有一项信息叫常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容在类加载之后被放到方法区的运行时常量池中,所以此内存区也是线程共享的内存,会抛出OutOfMemoryError异常。