Java 虚拟机将 Java 程序执行的区域称为运行时数据区,根据各自功能不同将运行时数据区划分为若干个不同的区域,
具体分为两大块,线程共享部分和线程私有部分。线程共享部分可以分为堆、方法区(jdk1.8 后这块区域被称为元空间);线程私有部分可以分为虚拟机栈、本地方法栈和程序计数器
程序计数器
指向当前线程正在执行的字节码指令的地址,程序计数器是一块较小的内存空间,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、跳转、循环、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
Java 虚拟机栈
虚拟机栈描述的是 Java 方法执行的内存模型,每个方法被执行的时候,Java 虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完毕的时候,就对应着一个栈帧从入栈到出栈的过程。
栈帧
当执行一个方法,就将该方法的栈帧压入栈顶,方法执行完就退出栈,也即从方法集合中去掉。
栈帧包含如下几个部分
-
局部变量表:,用于存放方法参数和方法内部定义的局部变量。在 Java 程序编译为 Class 文件时就在方法的 code 属性的 max_locals 数据项中确定了该方法所需要分配的局部变量表的最大容量。局部变量可以存放基本数据类型(boolean、byte、char、short、int、float、long、double)和对象引用类型(reference)
-
操作数栈:存放方法执行、操作
-
动态链接:指向运行时常量池中该栈帧所属方法的引用
-
完成出口:在方法退出后都返回到该方法被调用的位置。方法正常退出时,调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。
本地方法栈
本地方法栈也是线程私有的部分,本地方法栈与虚拟机栈方法作用相似,其区别仅是虚拟机栈为 Java 方法服务,而本地方法栈则是为虚拟机使用到的本地方法服务。
Java 堆
存储我们创建的对象、数组
方法区
存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存数据,这些信息是由类加载时从类文件中提取出来的。
常量池解析
- 静态常量池
- 字面量:类的全路径字面值、字段名、字段字面值、方法名、方法入参
- 符号引用:Methodref、Fieldref、Class等。
//字面量
#57 = String #240 // login
#68 = Float 0.7f
//符号引用,方法信息
#4 = Methodref #75.#179 // java/lang/Object."<init>":()V
#14 = Class #192 // com/jd/pingou/base/jxutils/bu
- 运行时常量池
符号引用通常是用来设计在字符串上的,用文本形式来表示引用关系。而直接引用是JVM所能直接使用的形式。既可以表现为直接指针,也可以是其他形式。
符号引用在进行方法调用的时候,还需要解析符号引用所执行的代码,之后,才能执行