Java虚拟机规范
2020-12-08 11:19:42 10 举报
AI智能生成
登录查看完整内容
Java虚拟机规范
作者其他创作
大纲/内容
Java虚拟机规范
class文件格式
ClassFile结构
常量池中能保存的项类型。它们都是xx_info,由多个无符号和表组成
各种名称的内部表现形式
类和接口的二进制名使用全限定名。全限定名中的包分隔符用'/'(在.java文件中用'.'作包分隔符)
方法名,字段名,成员变量名用非限定名
描述符
字段描述符
字段描述符解释表
描述 int 实例变量的描述符是 IObject 类型的实例,其描述符是Ljava/lang/Object;三维数组 double d[][][]类型的实例变量,其描述符为[[[D
描述符具体格式见《Java虚拟机规范》P62
方法描述符
(参数描述符)返回值描述符
对于实例方法来说this也被作为参数计算在方法描述符的长度里面,但是this不在方法描述表达,参数this的传递由Java虚拟机调用实例方法时用指令实现
常量池
Java虚拟机代码约束
静态约束
结构化约束
加载、连接与初始化
运行时常量池
Java虚拟机如何从类或接口的二进制表示中得到符号引用
虚拟机启动
Java虚拟机启动时会有怎样的加载,链接和初始化过程
创建和加载
类或接口的二进制表示是如何通过类加载器加载并由此创建类和接口的
链接
描述链接过程
初始化
详述类和接口是怎么被初始化的
绑定本地方法实现
Java虚拟机绑定本地方法的概念
Java虚拟机退出
Java虚拟机退出的时机
一、Java虚拟机结构
class文件
数据类型
原始类型与值(也称原生类型或基本类型)
数值类型
整数类型
浮点类型
float,double零值是0
boolean
零值是falseJVM没有直接支持boolean的字节码指令,编译后boolean都被int类型代替但JVM直接支持boolean类型的数组,boolean数组会被编码为byte数组
returnAddress
是指向操作码的指针,操作码和JVM指令对应是原始类型中唯一不能直接与Java语言的数据类型对应的类型已经处于名存实亡的状态
引用类型与值
reference
“引用”的传统含义:`reference`类型的数据中存储的数值指定是另外一块内存的起始地址,就称为这块内存(`reference`类型的变量使用的内存)代表着一个“引用”
类类型—class type
值指向类实例
数组类型—array type
值指向数组实例
接口类型—interface type
值指向实现了某个接口的类实例或数组实例
引用的值可以为null,表示引用没有指向任何对象
运行时数据区
pc寄存器
每条线程都有一个pc寄存器当前线程正在执行的方法不是native的,pc寄存器保存的就是JVM正在执行的字节码的地址如果是native方法,pc寄存器的值就是undefined
Java虚拟机栈
每条线程都有自己私有的Java虚拟机栈,这个栈和线程同时创建你,用于存储栈帧
Java堆
线程共享。虚拟机启动时被创建
方法区
线程共享。保存运行时常量池,字段,方法,构造函数,普通方法的字节码和类,实例,接口初始化时用到的特殊方法是堆的逻辑组成部分,但此区域可以选择不实现垃圾回收和压缩
class文件常量池表的运行时表现形式包含从编译器可知的数值字面量到运行解析后才获取的方法或字段的引用
本地方法栈
native方法使用的栈
栈帧
创建栈帧时,局部变量表和操作数栈的容量就确定了确定容量大小的依据是方法表中携带的属性表的Code属性
局部变量表
局部变量表保存方法参数列表中的变量和方法体中定义的局部变量的引用
局部变量的索引以0开始计数,double和float占两个局部变量的位置,所以也占两个索引类的成员方法的局部变量表的第0个局部变量是this,存储调用实例方法的对象的引用
操作数栈
程序运行时保存计算得到的中间结果的栈
当前方法所属类的运行时常量池的引用
动态链接
将符号引用转为直接引用
方法调用正常完成
方法调用异常完成(抛出了在方法内未捕获的异常)
对象的表示
Java虚拟机规范不强制规定对象的内部结构应当如何表示
浮点算法
👴看不懂,跳过
特殊方法
他们都不是合法的java方法,不能通过java程序编码方式实现。只能被java虚拟机调用 <clinit>方法不能被字节码指令调用,只能被java虚拟机直接调用
实例的初始化方法(构造器)在java虚拟机中的名字是<init>
在虚拟机运行时被invokespecial指令调用
类或接口的初始化方法在java虚拟机中的名字是<clinit>
具有签名多态性的方法(略P18)
异常
抛异常的本质时程序控制权的一种即时的,非局部的转换
异常分同步异常和异步异常
异常出现的原因
athrow字节码指令被执行
产生同步异常
产生异步异常
虚拟机内部的异常也被视为异步异常
抛异常的影响的规范
在异常抛出,程序控制权发生转移的那一刻,所有在异常抛出前执行的字节码指令产生的影响都时可以被观察到的
如果执行的是优化后的代码(指令重排),有些在异常出现位置之后的代码可能已经被执行了,这些优化代码必须保证被提前执行所产生的影响对用户是不可见的
异常处理器表,在4.7.3中讲
字节码指令集简介
数据类型与Java虚拟机
加载和存储指令
用于将数据在栈帧的本地变量表和操作数栈之间来回传递
算术指令
用于对操作数栈中两个数据的计算,并将结果压入操作数栈
类型转换指令
用于Java虚拟机中不同数值类型数据之间的转换
对象的创建与操作
操作数栈管理指令
直接控制操作数栈
控制转移指令
方法调用和返回指令
抛出异常指令
同步指令
二、Java虚拟机编译器
编译器
JDK包括1. 将Java源代码编译为Java虚拟机的指令集的编译器2. 实现Java虚拟机的运行时环境Java编译器还包括JIT(将热点代码编译为机器码的编译器)但这里只讨论将Java源代码编译为Java虚拟机的指令集的编译器
常量,局部变量和控制结构的使用
算术运算
Java对数值类型的变量的算术运算通常在操作数栈中进行取出操作数栈中的数据并将结果压入栈中
访问运行时常量池
接收参数
如果传递了n个参数给某个实例方法,则当前栈帧会按照约定,一参数传递顺序来接收这些参数,将它们保存到方法的第1个至第n个局部变量之中
成员方法的局部变量表的第0个变量是this静态方法的局部变量表的第0个变量直接保存第一个参数
方法调用
invokespecial
静态绑定的方法调用指令包含实例初始化方法,父类(super)方法,final方法和私有方法
invokevirtual
动态绑定的方法调用指令
invokestatic
调用静态方法,它和invokespecial的区别在于,这个指令不需要this操作数作为参数而invokespecial需要this操作数作为参数
调用方法时使用的指令都需要有一个操作数来确定调用的是哪个方法如:invokespecial #4在常量池中#4对应的是方法的符号引用在运行时,这个符号引用会被转换为调用方法的实际地址(直接引用)符号引用转为直接引用的过程稍后再说
方法句柄的介绍
浅析方法句柄
在调用方法前需要传递调用方法的引用作为方法的第一个参数
使用类实例
创建类实例
new指令创建类实例并初始化实例变量的默认值
访问类实例的实例变量
getfield和putfield指令指令的操作数是类实例的字段,运行时字符引用被替换为直接引用(字段解析中执行的)这两个指令都需要实例的引用作为参数(看起来就像是通过this.filed访问的字段,即使没加this,编译器也能自动识别出来)
数组
编译switch语句
tableswitch和lookupswitch指令支持switch语句
tableswitch下的表中k是有序且递增,lookupswitch下的表中k是无序所以前者比后者效率高(前者匹配case的方式为范围匹配,匹配速度快)
使用操作数栈
Java虚拟机是基于栈的虚拟机
抛出异常和处理异常
异常处理表的from和to是左闭右开区间
编译finally语句块
finally在编译时会添加一个特殊的异常处理器,这个异常处理器可以捕获try快中抛出的所有异常
同步
同步方法的同步由常量池中的ACC_SYNCHRONIZED标志来隐式实现同步块的同步由monitorenter和monitorexit字节码指令实现
monitorenter和monitorexit都需要栈顶一个对象的引用。使用该对象的内部锁完成同步
同步代码块在编译时会添加一个捕获所有异常的异常处理器,执行释放锁的操作,用于当抛出异常时还能保证执行monitorexit释放锁
注解
0 条评论
回复 删除
下一页