JVM运行时的数据区域
@Date:2018-5-16
@Author:LF
首先,上个图,直观感受一下(图片来自博客【如果作者觉得侵权私聊我删除】https://www.cnblogs.com/xlyslr/p/5673057.html)
接下来则分块理解各个区域的作用
pc寄存器(程序计数器)–线程私有
程序计数器感觉比较类似于广义上的寄存器。那么问题来了:
寄存什么?
- 答:寄存指令
什么指令?
- 答:当前线程正在执行的方法所对应的字节码的指令
为什么要记录这个指令?计数器存在的意义是什么?
- 答:为了针对多线程的程序。JVM的多线程实际上是通过线程轮流切换分配处理器时间实现,为了区分每一个方法当前执行的是什么就需要一个东西存储线程当前时刻在干什么,这样可以保证在线程切换后可以继续运行之前停止前正在运行的指令。
JVM栈(VM Stack)–线程私有
栈帧与线程中的方法对应,一个方法就会有一个对应的栈帧。栈帧中存储方法的所有信息。因为栈的先进后出的性质,故在方法中调用其他方法时,心得方法栈帧入栈作为当前栈帧(栈顶帧),方法结束时则从方法里向外结束,栈帧依次出栈。
1.局部变量表
基本单位:容量槽(slot),一个slot无明确的大小,但是每个slot都能存放下java的8种基本数据。
局部变量表的实质是一片存储空间,用来存储方法参数和方法内部定义的局部变量。
JVM通过索引的方式访问局部变量表
slot的复用特性:
- 当方法中的变量作用域结束而方法还未完全结束时,此刻其实前面的变量已经不会再用到了,因为变量作用域已经结束了嘛,所以其slot可以腾出来给其他变量使用,这就是slot的复用特性。目的是为了节省栈空间。
2.操作数栈
方法运行时,会有各种字节码指令往操作数栈中写入或读出内容。也就是说操作数栈是用来存放一些值的。
如果说局部变量表理解为方法内所有参数和定义的局部变量的存储空间,那么操作数栈则是方法执行时候的各个值的存储空间或者说是缓冲空间,感觉作用和PC计数器差不多。
3.动态链接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用。这就叫动态链接。
如何理解这句话?
在方法执行中,当需要另一个方法被调用时,其实就是在当前方法中使用了被调用方法在运行时常量的引用。该引用指向被调用方法栈帧,通过该引用将栈帧入JVM栈。
也就是说可以把动态链接理解为一种绑定标记,和运行时常量池中对该方法的符号引用进行标记。我是这么理解的。
运行时常量池中的符号引用会有两种绑定方式(方法的绑定)。简单来说,当类加载阶段可以确定的方法(比如不存在重载),即可以在类加载时将符号引用直接转化为直接引用,这就叫做静态解析。
但是如果遇到在类加载时候无法确定被调用的目标方法(比如存在重载,根据具体的方法参数确定是调用哪个方法的时候),必须通过运行时确定调用的方法,确定后才将符号引用转化为直接引用,这就叫做动态链接。
动态链接的存在解决的就是在运行时将符号引用转化为直接引用的问题。
4.方法返回值
顾名思义,方法返回值存储的就是当前方法的返回值,当前方法结束后,栈帧出战,其返回值会被压入当前栈帧的操作数栈。应该是比较好理解的。
本地方法栈(Native Method Stack)–线程私有
用于支持本地方法,即非java方法,其作用与JVM栈类似,很多虚拟机(如Sun公司的HotSpot)会将这两种栈合二为一。
Java堆(Java heap)–线程共享
唯一作用:存放对象实例
但是并不是所有对象都放在Java堆上
而且Java堆的内存不连续,是GC(garbage collection)重点回收对象
其实说白了,就是java运行时的堆空间嘛。
方法区–线程共享
用来存储已经被JVM加载的类信息、常量、静态变量等。和java堆一样拥有不连续的内存区域。而且运行时常量池就属于方法区。