1. 运行时数据区的结构
每个JVM只对应一个Runtime实例
2. 线程
JVM虚拟机允许一个应用程序创建多个线程并行执行
在HotSpot JVM中,每个线程都和本地操作系统本地线程进行直接的映射
3.程序计数器
1).什么是程序计数器?
JVM中的PC寄存器是物理机器上的PC寄存器的一个模拟,存放下一条指令的位置
2).作用
存储下一条指令的地址
ps:线程私有的,无GC,无OOM,其中native方法在PC中为undefined
3)常见面试问题
- 使用PC寄存器存储字节码指令地址有什么用?
因为CPU在不断的切换进程,用PC寄存,就能在切换回来的时候知道从哪里继续执行程序
- 为什么PC寄存器是线程私有的
若不是私有的就会导致指令错乱,可能在CPU切换的时候运行别人的指令
4.虚拟机栈
1).什么是虚拟机栈?
主管Java程序的运行,它保存方法的局部变量,部分结果并参与方法的调用和返回
2).栈帧
虚拟机栈的基本单位,一个栈帧对于一个方法
ps: 虚拟机栈是线程私有的
- 局部变量表
- 操作数栈
- 动态链接(指向运行时常量池的方法引用)
- 方法返回地址(方法正常退出或异常退出的定义)
3).开发中遇到那些异常?
允许设置Java栈为固定值,若超过设置的最大容量就会发送StackOverflowError
若动态添加,在扩容内存大于能分配的内存的时候就会产生OutOfMemoryError
4)设置参数
-Xss 设置最大的栈大小
如-Xss 256k,默认大小约为linux 1024KB Mac 1024KB
5). 一些附加信息
①.局部变量表
定义一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据包括基本数据类型,对象引用以及returnaddress类型,在编译时就确定下来了,不存在数据安全问题
最基本的单位slot,32位以内的数据类型只占用一个slot,64位的占用2个slot
在构造方法或者实例方法中会将对象引用this存放在index为0的slot中,其余参数按照顺序排序。
slot可以重复利用,局部变量使用前必须显式赋值
<font color="red">局部变量表是重要的GCRoot,只要被局部变量表直接或者间接引用的变量不会被GC回收</font>
②.操作数栈
基本单位为栈深度,数组实现的栈
栈顶缓存技术,缓存在cpu的寄存器,降低内存的IO次数,提升执行引擎的效率
③.动态链接
在JVM中,将符号引用转换为调用方法的直接引用与方法绑定机制有关。
动态链接
只是在运行时可知,对应的方法绑定机制为:早期绑定和晚期绑定,绑定时一个字段、方法或者类的符号引用呗替换为直接引用的过程,这仅仅发生一次- 早期绑定(如调用父类构造器)
目标方法在编译器可知,在运行期不变 - 晚期绑定(如父类调用方法等)
与之相反
- 早期绑定(如调用父类构造器)
- 静态链接
一个字节码文件被转载如虚拟机时,如果被调用目标方法在编译器可知
具备多态性就具有早期绑定和晚期绑定两种方法
5.虚方法和非虚方法
1).非虚方法:方法在编译期间就确定好了调用版本,这个版本运行时是不可变的
如:静态方法,私有方法,final方法,实例构造器,父类方法都是非虚方法
2).虚方法
其他方法均称为虚方法
6.方法重写的本质
step1.找到操作数栈栈顶的第一个元素所执行的对象实际类型记为C
step2.若类型C中找到常量中的描述符合并且简单名称都符合的方法,则进行访问权限校验,如果通过返回这个方法的直接引用,查找过程结束
step3.否则按照继承关系从下往上一次对各个父类进行搜索和验证过程
step4.始终未找到,则抛出java.lang.AbstarctMethodError
<font color='red'>每次调用都会进行这么一个过程,为了提升性能,JVM在类中方法区建立一个虚方法表,来用索引代替查找</font>
方法返回地址
1)作用
存放该方法PCC寄存器的值
2).方法结束的方式
①正常执行完成
②出现未处理的异常
无论哪种方式退出在方法退出后都会返回到该方法被调用的位置,方法正常退出,调用者的PC寄存器的值作为返回地址,即调用下一条指令的地址。而异常退出,返回地址是通过异常表确定的,栈帧中一般不会保存这部分信息