1、pc计数器
程序计数器的作用是什么?
- 字节码解释器通过改变程序计数器中存储的下一条字节码指令地址以此来达到流程控制
- Java多线程的线程会切换,为了保存线程切换前的正确执行位置,每个线程都应该有程序计数器,因此程序计数器是线程私有的 (私有是因为线程的数据结构维护了一个关于pc计数器的字段?所以私有?)pc计数器不会抛出异常,也没有垃圾回收。
- 执行本地方法时,程序计数器记录的是空
2、本地方法栈
**执行非java语言编写的方法时用到的栈,如c **
3、虚拟机栈
每当一个方法开始执行,就会方法所对应的栈帧入虚拟机栈。方法中调用其他方法 就会开始压栈。
3.1、栈帧的结构
** 栈帧包括返回地址、局部变量表、动态连接、一些附加信息、操作数栈**
理解:栈帧相当于一个类,包含的返回地址、局部变量表、动态连接、一些附加信息、操作数栈相当于类的属性。
3.1.1、返回地址
执行方法后,有两种方式可以退出
1、正常调用完成
2、异常调用完成
- 正常调用完成: 遇到方法返回的字节码指令
- 方法退出有时需要在栈帧中保存一些信息以恢复上一层方法的执行状态(程序计数器的值)
- 异常调用完成: 遇到异常未捕获(未搜索到匹配的异常处理器)
- 以异常调用完成方式退出方法,不会在栈帧中保存信息,通过异常处理器来确定(不懂)
3.1.2、 附加信息
增加一些《Java虚拟机规范》中没有描述的信息在栈帧中(取决于具体虚拟机实现)
3.1.3、动态连接
动态连接:栈帧中指向运行时常量池所属方法的引用**
运行时常量池:jdk1.8后运行时常量池放在方法区中,字符串常量池放在堆中,方法区的具体实现是元空间(堆外内存)
静态解析与动态连接
符号引用转换为直接引用有两种方式
- 静态解析:在类加载时解析阶段将符号引用解析为直接引用
- 动态连接:每次运行期间把符号引用解析为直接引用(因为只有在运行时才知道到底指向哪个方法)
- 为什么是动态连接能实现运行时才确定调用那个方法,主要是与invokevirtual指令有关(看深入理解jvm虚拟机310-311页)。
3.1.4、局部变量表
局部变量表用于存放方法中的实际参数
和方法内部定义的变量
以局部变量槽为单位(编译期间就确定了)
每个局部变量槽都可以存放**byte,short,int,float,boolean,reference,returnAddress
**
byte,short,char,boolean在存储前转为int (boolean:0为false 非0为true)
而double,long
由 两个局部变量槽存放
每个局部变量槽的真正大小应该是由JVM来决定的
在构造方法和非静态方法中,this是局部变量表的第一个参数。
3.1.5、操作数栈
操作数栈就相当于cpu的寄存器,jvm是栈式架构,当方法开始执行时,操作数栈是空的,执行引擎开始执行,才会有各种各样的字节码指令往操作数栈中执行入栈和出栈操作。