第一章:走进Java
第二章:Java内存区域与内存溢出异常
第三章:垃圾收集器与内存分配策略
第四章:虚拟机性能监控与故障处理
第五章:调优案例分析与实战
第六章:类文件结构
第七章:虚拟机类加载机制
第八章:虚拟机字节码执行引
第九章:类加载及其执行子系统的案例与实战
第十章:早期(编译器)优化
第十一章:晚期(运行期)优化
第十二章:Java内存模型与线程
第十三章:线程安全与锁优化
执行引擎:输入字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果
栈帧:是虚拟机用于方法调用和方法执行的数据结构,是虚拟机运行时数据区的虚拟机栈的栈元素
栈帧存储了方法的局部变量表、操作数栈、动态连接、方法返回地址等信息
每一个方法从调用开始到执行结束,就是栈帧在虚拟机栈中入栈出栈的过程
在编译期间,栈帧需要多大的局部变量表、多深的操作数栈都已经完全确定,并且写入方法的code 属性中
在活动线程中,栈顶的栈帧才是有效的,称为当前栈帧,相关联的方法称为当前方法
是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量
在Java程序编译为class时,就在方法的code属性的max_locals数据项中确定了该方法所需分配的局部变量表的最大容量
局部变量表的容量以变量槽slot为最小单位
一个slot可以存放一个32位以内的数据,Java中32位以内的数据类型有:boolean、byte、char、short、int、float、reference、returnAddress
通过Reference类型可以:
对于64位(long,double)的数据类型,虚拟机采用高位对齐的方式为其分配两个slot空间
虚拟机通过索引定位的方式使用局部变量表
方法执行过程中,虚拟机通过局部变量表完成变量值到参数列表的传递过程
对于实例方法,局部变量表中第0位索引的slot默认传递方法所属对象实例的引用
为了节省栈帧空间,slot空间可以重用,但是会有额外的副作用,例如影响垃圾收集
是一个后入先出栈
在编译时候写入code属性的max_stacks数据项中
操作数栈的每一个元素可以是任意的Java数据类型
32位数据类型占用的栈容量为1,64位数据类型占用的栈容量为2
操作数栈中元素的数据类型和字节码指令的序列严格匹配
概念模型上,两个栈帧完全相互独立,但大多虚拟机做了优化处理,使两个栈帧出现一部分重叠(方法调用可以共用一部分数据,无需进行额外的参数复制传递)
每个栈帧都有一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接
正常完成出口:PC计数器的值可以作为返回地址,栈帧中很可能保存这个计数值
异常完成出口:通过异常处理器表来确定返回地址,栈帧中一般不会保存这部分信息
方法退出等同于当前栈帧出栈,可能执行的操作有:
恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令
方法调用不等同于方法执行,唯一任务就是确定被调用方法的版本
调用目标在程序写好、编译器进行编译时就必须确定下来,这类方法的调用称为解析
所有方法调用中的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用。
编译期可知,运行期不变的方法:静态方法、私有方法
相对应的5条方法调用字节码指令
只要能被invokestatic和invokespecial指令调用的方法,都可以再解析阶段中确定唯一的调用版本,符合这个条件的由静态方法、构造方法、私有方法、父类方法四大类,这些方法称为非虚方法(还包含final修饰的方法,无法被覆盖,没有其他版本);与之相反的称为虚方法(final修饰除外)
静态分派(重载)
静态类型(外观类型):变量本身的静态类型不会被改变,最终的静态类型是在编译期可知的
实际类型:变化结果在运行期才确定
使用哪个版本的重载,完全取决于传入参数的数量和数据类型;虚拟机(准确的说是编译器)在重载时是通过参数的静态类型而不是实际类型作为判定依据;并且静态类型是编译期可知的,因此,在编译阶段,javac编译器会根据参数的静态类型决定使用哪个版本的重载
所有依赖静态类型来定位方法执行版本的分派称为静态分派
静态分派的典型应用就是方法重载
动态分派(重写)
invokevirtual的运行时解析过程:
找到操作数栈顶的第一个元素指向的对象的实际类型,记作C
如果类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束,如果不存在,则返回java.lang.IllegalAccessError异常
否则,按照继承关系从下往上依次对C的各个父类进行第二步的查搜索和验证过程
如果始终没有找到,则抛出java.lang.AbstractMethodError异常
运行期根据实际类型确定方法版本的分派称为动态分派
单分派和多分派
方法的接受者方法的参数统称为方法的宗量,根据分派基于多少种宗量,可以将分派划分为单分派和多分派
单分派是根据一个宗量对目标方法进行选择
多分派是根据多于一个宗量对目标方法进行分派
静态分派属于多分派
动态分派属于单分派
虚拟机动态分派的实现
方法表
动态类型语言
关键特征是类型检查的主体过程是在运行期而不是编译期
变量无类型而变量值有类型
静态类型语言在编译期提供严谨的类型检查
动态类型语言提供了更大的灵活性
JDK7与动态类型语言
invokedynamic指令以及java.lang.invoke包出现
java.lang.invoke包
提供了一种动态确定目标方法的机制,称为MethodHandle
MethodHandle和反射(Reflection)区别:
本质上将都是在模拟方法调用,但反射模拟Java代码层次的调用,MethodHandle模拟字节码层次的调用
反射中的java.lang.Method对象远比MethodHandle机制的java.lang.MethodHandle对象所彪悍的信息多,反射是重量级,MethodHandle是轻量级
MethodHandle优化
invokedynamic指令
每一处含有invokedynamic指令的地方都称为动态调用点
掌控方法分派规则
javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树,在遍历语法树生成线性的字节码指令流的过程
一部分在虚拟机之外进行,而解释器是在虚拟机内部,所以Java程序的编译是半独立的实现
基于栈的指令集主要优点是可移植性、代码更加紧凑、编译实现更简单;缺点是执行速度相对慢点