什么是虚拟机?
通过软件模拟的具有完整硬件功能的系统功能的、运行在一个完全隔离环境中的计算机系统。
JVM通过模拟Java字节码文件指令集。是Java程序。
栈帧
存储数据的单元
-
1.局部变量
1、一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。
2、Class文 件中方法表的Code属性的max_ locals数据项中定义了该表的容量最大值。
3、该表的容量计量单位为slot,一个slot可以存放-一个32位以内的数据(因 此可以存放boolean、int、
char、byte等, 引用类型可能是32位也可能是64位)。java语言中,只有long和double为64位类型,
占用两个slot。
4、虚拟机通过索引的方式使用局部变量表,索引值从0开始。方法执行时,索引为0的slot默认用于
传递方法所属对象的引用,方法中可以使用this关键字来访问这个隐含的参数。
5、为了节省空间,表中的slot是可以重用的(这可能会直接影响到垃圾回收行为)。- 8大类型地址
- this
- object地址
- 8大类型地址
-
2.操作数栈
1、操作数栈也常称操作栈,是一个后入先出的栈。
2、操作数栈的最大深度在编译的时候已经写入方法表的Code属性的max_ _stacks数 据项中。
3、操作数栈的每一个元素可以是任意的JAVA数据类型,包括long和double。 32位数据占用栈空间为
1,64位数据占用栈空间为2。
4、方法刚开始执行时,操作数栈是空的,方法执行过程中,会有各种字节码指令往操作数栈中存
取数据。
5、操作数栈中元素的数据类型必须与字节码指令的序列严格匹配。例如iadd指令用于执行整数加法,
一定不能用 于操作一个long - -个double的情况。 -
3.动态链接
1、 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方
法调用过程中的动态链接。
2、Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的
符号引用作为参数。这些符号引用一部分会在类加载阶段或者第一次使用的时候转化为直接引用,
这种转化成为静态解析。另一部分将会在每一次运行期间转化为直接引用,这部分成为动态链接。- 多态 编译时才知道
-
4.方法出口
1、当一个方法开始执行之后,只有两种方式可以退出这个方法。
2、第一种方式是执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给
上层的方法调用者,是否有返回值以及返回值类型将根据遇到的方法返回字节码指令来决定,这
种退出方式成为正常完成出口。
3、另一种退出方式是在方法的执行过程中出现了异常,并且这个异常没有在方法体内得到处理,
无论是JVM内部产生的异常还是代码中使用throw关键字产生的异常,只要在本方法的异常处理表
中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方式称为异常完成出口。此种情
况下,方法是不会给上层调用者返回任何值得。
4、无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置继续执行,方法
返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。
5、方法退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈、把返回值(如果有)
压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一-条指令地址。
程序计数器
JVM中的字节码执行引擎
执行引擎:读取运行时数据区的Java字节码并逐行执行
- JIT (一次编译,保存在内存中)
- 字节码执行引擎 (即使编译)
- mixed mode (混合方法)
JVM类加载机制
- 引导类加载器
- 拓展类加载器
- 系统类加载器
- 自定义加载器
- 系统类加载器
- 拓展类加载器
双亲委托模式优势
- 沙箱安全机制:自己写的String.class类不会被加载,这样便可以防止核心API库被随意篡改
- 避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再 加载一次