jvm内存模型由方法区,堆、虚拟机栈、本地方法栈、程序计数器,
1.程序计数器
每个线程都会有自己私有的程序计数器,可以看做当前线程所执行的字节码的行号指示器。
字节码解释器就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程上下文切换。
线程恢复时,都要依赖PC 。
如果线程正在执行的是一个java方法,pc值为正在执行的虚拟机字节码指令的地址。
如果线程正在执行的是native方法,PC值为null。
3.虚拟机栈
当创建一个线程时,jvm就会为这个线程创建一个对应的java栈,在这个java栈中又会有多个栈帧,这些栈帧是与每个方法关联起来的,每运行一个方法,就创建一个栈帧,
每个栈帧会含有一些局部变量(在方法内定义的变量)、操作栈、和方法返回值等信息。
VM stack 也是线程私有的区域。他是java方法执行的字典:它里面记录了局部变量表、操作数栈、动态链接、方法出口等。
Frame(帧栈)是用来存储数据结构和部分过程结果的数据结构,同时也被用来处理动态链接、方法返回值和异常分派栈帧,随着方法调用而创建,
无论是方法是正常完成还是异常完成都算作方法结束。
(帧栈):用于虚拟机执行时方法调用和方法执行时的数据结构,他是虚拟栈数据区的组成元素
每一个方法从调用到返回都对应着一个栈帧入栈出栈的过程。
每一个栈帧在编译程序代码的时候所需要的多大的局部变量表,多深的操作数栈都已经决定了,并且写入到发表
的code属性之中,一次一个栈帧需要多少内存,不会受到程序运行期变量数据的影响,仅仅取决对
具体的虚拟机实现。
一个线程中方法调用可能很长,很多方法都处于执行状态,对于执行引擎来说,只有处于栈顶
的栈帧才是有效的,称为当前帧栈,与之相关联的方法称之为当前方法。
概念模型上,典型的帧栈主要由局部变量表(Local Stack Frame)、 操作数栈、动态链接、
返回地址等。
局部变量表: 是一组变量值的存储空间,用于存放方法参数和局部变量,在Classs文件的方法表
的code属性的max_locals指定了该方法所需局部变量表的最大容量。
变量槽: 是局部变量表的最小单位,即虚拟机分配最小的变量存储单元,对于64位的
long 和 double变量而言,虚拟机会为其分配两个连续的Slot空间。
虚拟机通过索引定位的的方式使用局部变量,之前我们知道,局部变量存放的是方法参数和
局部变量,当使用非静态方法是,局部变量表中第0位索引的SLot默认是传递方法所属对象实例的引用,
即“this”关键字所指向的对象,分配完方法参数后,便会依次分配方法内部定义的局部变量。
栈帧的存储空间分配在java虚拟机栈之中,每一个栈帧都有自己 的局部变量、操作数栈、和指向当前方法所属的类的运行时常量池的引用。
为了节省栈帧空间,局部变量表中的Slot是可以重用的,当离开了某些变量的额作用域之后,这些变量的对应的slot
就可以交给其他变量使用,这种机制有时候会影响垃圾回收行为。
操作数栈: 操作数栈是一个后入先出栈,在Class文件的code属性的max_stacks指定了执行过程中最大的
栈深度,java虚拟机的解释执行引擎称为“基于栈的执行引擎”,这里的栈就是操作数栈。
方法执行中进行算数运算或者调用其他的方法进行参数传递的时候是通过操作数栈进行的。
在概念模型中,两个栈帧是相互独立的,但是大多数虚拟机的实现都会进行优化,令
两个栈帧出现一部分重叠,令下面的部分操作数栈与上面的局部变量表重叠在一块,这样在方法调用的时候可以共用一部分数据,无需进行额外的额参数复制传递。
3.动态链接:
每个栈帧都包含一个执行运行时常量池中该栈帧所属方法的引用
4.本地方法栈(native Method Stack)
VM Stack是为java方法服务的,此处的Nactive Method是为执行本地方法服务的。
此处的本地方法指定是和具体的底层操作系统层面相关的接口调用了……
5.Java堆
在java虚拟机中,堆(Heap)是可供各条线程共享的运行时内存区域,也是供所有类实例和数组对象分配内存的区域。
在虚拟机启动时被创建
是所有线程共享的内存区域
存储了被自动内存管理系统所管理的各种对象。
这些被管理的对象无需,也无法显式的被销毁
自动内存管理系统并未指明用什么技术去实现自动内存管理系统。
java堆所使用的内存不需要保证是连续的。
6.方法区
方法区是由所有线程共享的内存区域
方法区存储的大致内容如下:
每一个类的结构信息:
运行时常量池
字段和方法数据
构造函数和普通方法的字节码内容
类、实例、接口初始化时用到的特殊方法。
每一个运行时常量池都分配在java虚拟机的方法区之中,在类和接口被加载到虚拟机之后,对应的运行时常量池就被创建出来。
7.直接内存
此处的直接内存并不是有jvm管理的内存,它是利用本地方法库直接在java堆之外申请的内存区域。
比如NIO的DirectByteBuff就是操作直接内存的。
直接内存的好处就是避免了在java堆和native堆直接同步数据的步骤。但是他并不是由jvm来管理的。