Java虚拟机规范(JavaSE7) 笔记
引用类型与值
Java虚拟机中有三种引用类型:类类型(Class Types)、数组类型(Array Types)和接口类型(Interface Types)。这些引用类型的值分别由类实例、数组实例和实现了某个接口的类实例或数组实例动态创建。
异常
Java虚拟机中异常的出现总是由下面三种原因之一导致的:
1. 虚拟机同步检测到程序发生了非正常的执行情况,这时异常将会紧接着在发生非正常执行情况的字节码指令之后抛出。例如:
字节码指令所蕴含的操作违反了Java语言的语义,如访问一个超出数组边界范围的元素。
类在加载或者链接时出现错误。
使用某些资源的时候产生资源限制,例如使用了太多的内存。
2. athrow字节码指令被执行。
3. 由于以下原因,导致了异步异常的出现:
调用了Thread或者ThreadGroup的stop方法。
Java虚拟机实现的内部程序错误。
当某条线程调用了stop方法时,将会影响到其他的线程,或者在线程组中的所有线程。这时候其他线程中出现的异常就是异步异常,因为这些异常可能出现在程序执行过程的任何位置。虚拟机的内部异常也被认为是一种异步异常
字节码指令集
字节码指令流应当都是单字节对齐的,只有“tableswitch”和“lookupswitch”两条指令例外,由于它们的操作数比较特殊,都是以4字节为界划分开的,所以这两条指令那个也需要预留出相应的空位来实现对齐。
限制Java虚拟机操作码的长度为一个字节,并且放弃了编译后代码的参数长度对齐,是为了尽可能地获得短小精干的编译代码,即使这可能会让Java虚拟机的具体实现付出一定的性能成本为代价。由于每个操作码只能有一个字节长度,所以直接限制了整个指令集的数量,又由于没有假设数据是对齐好的,这就意味着虚拟机处理那些超过一个字节的数据的时候,不得不在运行时从字节中重建出具体数据的结构,这在某种程度上会损失一些性能。字节码无法超过256条的限制就来源于此。
数据类型与Java虚拟机
Java虚拟机的指令集对于特定的操作只提供了有限的类型相关指令去支持它,换句话说,指令集将会故意被设计成非完全独立的(Not Orthogonal,即并非每种数据类型和每一种操作都有对应的指令)。有一些单独的指令可以在必要的时候用来将一些不支持的类型转换为可被支持的类型。
大部分的指令都没有支持整数类型byte、char和short,甚至没有任何指令支持boolean类型。编译器会在编译期或运行期会将byte和short类型的数据带符号扩展(Sign-Extend)为相应的int类型数据,将boolean和char类型数据零位扩展(Zero-Extend)为相应的int类型数据。
因此,大多数对于boolean、byte、short和char类型数据的操作,实际上都是使用相应的对int类型作为运算类型(Computational Type)。
同步
Java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor)来支持的。当方法调用时,调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程将先持有管程,然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放管程。在方法执行期间,执行线程持有了管程,其他任何线程都无法再获得同一个管程。如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的管程将在异常抛到同步方法之外时自动释放。
结构化锁定(Structured Locking)是指在方法调用期间每一个管程退出都与前面的管程进入相匹配的情形。因为无法保证所有提交给Java虚拟机执行的代码都满足结构化锁定,所以Java虚拟机允许(但不强制要求)通过以下两条规则来保证结构化锁定成立。假设T代表一条线程,M代表一个管程的话:
1. T在方法执行时持有管程M的次数必须与T在方法完成(包括正常和非正常完成)时释放管程M的次数相等。
2. 找方法调用过程中,任何时刻都不会出现线程T释放管程M的次数比T持有管程M次数多的情况。
请注意,在同步方法调用时自动持有和释放管程的过程也被认为是在方法调用期间发生。
公有设计,私有实现
实现者可以使用这种伸缩性来让Java虚拟机获得更高的性能、更低的内存消耗或者更好的可移植性,选择哪种特性取决于Java虚拟机实现的目标和关注点是什么,虚拟机实现的方式主要有以下两种:
将输入的Java虚拟机代码在加载时或执行时翻译成另外一种虚拟机的指令集
将输入的Java虚拟机代码在加载时或执行时翻译成宿主机CPU的本地指令集(有时候被称Just-In-Time代码生成或JIT代码生成)
精确定义的虚拟机和目标文件格式不应当对虚拟机实现者的创造性产生太多的限制,Java虚拟机是被设计成可以允许有众多不同的实现,并且各种实现可以在保持兼容性的同时提供不同的新的、有趣的解决方案。
加载是根据特定名称查找类或接口类型的二进制表示(Binary Representation),并由此二进制表示创建类或接口的过程。
链接是为了让类或接口可以被Java虚拟机执行,而将类或接口并入虚拟机运行时状态的过程。
虚拟机启动
Java虚拟机的启动是通过引导类加载器(Bootstrap Class Loader §5.3.1)创建一个初始类(Initial Class)来完成,这个类是由虚拟机的具体实现指定。紧接着,Java虚拟机链接这个初始类,初始化并调用它的public void main(String[])方法。之后的整个执行过程都是由对此方法的调用开始。执行main方法中的Java虚拟机指令可能会导致Java虚拟机链接另外的一些类或接口,也可能会调用另外的方法。
创建和加载
Java虚拟机支持两种类加载器:Java虚拟机提供的引导类加载器(Bootstrap Class Loader)和用户自定义类加载器(User-Defined Class Loader)。每个用户自定义的类加载器应该是抽象类ClassLoader的某个子类的实例。应用程序使用用户自定义类加载器是为了便于扩展Java虚拟机的功能,支持动态加载并创建类。
posted on 2018-02-22 17:05 Lv Jianwei 阅读(184) 评论(0) 编辑 收藏 举报