深入理解Java之java虚拟机干净利落的规范总结 上

时间:2021-07-16 | 标签: | 作者:Q8 | 来源:网络

小提示:您能找到这篇{深入理解Java之java虚拟机干净利落的规范总结 上}绝对不是偶然,我们能帮您找到潜在客户,解决您的困扰。如果您对本页介绍的深入理解Java之java虚拟机干净利落的规范总结 上内容感兴趣,有相关需求意向欢迎拨打我们的服务热线,或留言咨询,我们将第一时间联系您!

   

  要去正确地实现一台Java虚拟机,就需要正确地读取class文件中每一条字节码指令并且能正确执行这些指令所蕴含的操作即可。
  数据类型
  和Ja海外贸易va语言类似,在Java虚拟机中的数据类型也可以分为基本类型和引用类型两种,所以也存在原始值和引用值两种类型的数值。它们可用于变量赋值、参数传递、方法返回和运算操作。
  原始类型与值
  Java虚拟机所支持的原始数据类型包括数值类型、boolean类型、和returnAddress类型
  数值类型分为整数类型和浮点类型,分别是char,byte,short,int,long;浮点类型即float和double,这里和Java语言中的一致。
  returnAddress翻译过来是返回地址,其实returnAddress类型的值指向一条虚拟机指令的操作码。它在虚拟机中比较典型的一个应用场景是用于jsr程序段落跳转,在try-catch异常处理以及finally代码块经常出现。和数值类的原生类型不同,returnAddress类型在Java语言之中并不存在相应的类型,而且也无法在程序运行期间修改。
  虽然Java虚拟机定义了boolean这种数据类型,但是只对它提供了十分有限的支持。在Java虚拟机中并没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替。(Java虚拟机会把boolean数组元素中的true采用1来表示,false采用0来表示,当Java编译器把Java语言中的boolean类型值映射为Java虚拟机的int类型值时,也必须用上述表示方式)
  引用类型与值
  Java虚拟机中有三种引用类型:类类型、数组类型和接口类型。它们分别指向动态创建的类实例、数组实例和某个接口的类实例或数组实例。
  数组类型最外面那一维元素的类型叫做数组类型的组件类型。一个数组的组件类型也可以是数组。从任意一个数组开始,如果发现其组件类型也是数组类型,那就继续取这个小数组的组件类型,不断执行这样的操作,最终一定可以遇到组件类型不是数组的情况,这时就把这种类型成为本数组的元素类型。数组的元素类型必须是原生类型、类类型或者接口类型之一。
  在引用类型的值中还有一个特殊的值:null,当一个引用不指向任何对象的时候,它的值就用null来表示。一个为null的引用,起初并不具备任何实际的运行期类型,但是它可转型为任意的引用类型。引用类型的默认值就是null。Java虚拟机规范并没有规定null在虚拟机实现中应当怎样用编码来表示。
  运行时数据区域
  栈帧:栈帧是用来存储数据和部分过程结果的数据结构,同时也用来处理动态链接、方法返回值和异常分派。栈帧又是存储在栈中(包括Java虚拟机栈和本地方法栈),它随着方法调用而创建,随着方法结束而销毁,其实也就是一个方法执行的过程也对应着栈帧的入栈和出栈的过程。无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。栈帧的存储空间由创建它的线程分配在Java虚拟机栈之中,每一个栈帧都有自己的本地变量表、操作数栈和指向当前方法所属的类的运行时常量池的引用。
  本地变量表和操作数栈的容量在编译期确定,并通过相关方法的code属性保存及提供给栈帧使用。因此,栈帧数据结构的大小仅仅取决于Java虚拟机的实现。实现者可以在调用方法的时候给它们分配内存。
  在某条线程执行的过程中的某个时间点,只有目前正在执行的那个方法的栈帧是活动的。这个栈帧称为当前栈帧,这个栈帧对应的方法称为当前方法,定义这个方法的类称作当前类。对局部变量表和操作数栈的各种操作,通常都是值对当前栈帧的局部变量表和操作数栈所进行的操作。
  如果当前方法调用了其他方法,或者当前方法执行结束,那这个方法的栈帧就不再是当前栈帧了。调用新方法时,新的栈帧也会随之而创建,并且会随着程序控制权移交到新方法而成为新的当前栈帧。方法返回之际,当前栈帧会传回此方法给前一个栈帧,然后虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧。
  需要特别注意的是,栈帧是线程本地私有的数据,不可能在一个栈帧之中引用另外一个线程的栈帧。
  局部变量表
  每个栈帧内部都包含一组称为局部变量表的变量列表。栈帧中局部变量表的长度由编译器决定,并却存储于类或接口的二进制表示之中,即通过方法的code属性保存及提供给栈帧使用。
  一个局部变量可以保存一个类型为boolean、byte、char、short、int、float、reference或returnAddress的数据。两个局部变量可以保存一个类型为long或double的数据。
  局部变量使用索引来进行定位访问。首个局部变量的索引值为0。局部变量的索引值是个整数,它大于等于0,且小于局部变量表的长度。Java虚拟机使用局部变量表来完成方法调用时的参数传递。当调用类方法时,它的参数将会依次传递到局部变量表中从0开始的连续位置上。当调用实例方法时,第0个局部变量一定用来存储该实例方法所在对象的引用(即Java语言中的this关键字)。后续其他参数将会传递至局部变量表中从1开始的连续位置上。
  操作数栈:每个栈帧内部都包含一个称为操作数栈的后进后出栈。栈帧中操作数栈的最大深度由编译期决定,并且通过方法的code属性保存及提供给栈帧使用。栈帧刚创建的时候操作数栈是空的。Java虚拟机提供一些字节码指令来从局部变量表或者对象实例的字段中复制常量或变量值到操作数栈中,也提供了一些指令用于从操作数栈取走数据、操作数据以及把操作结果重新入栈。在调用方法时,操作数栈也用来准备调用方法的参数以及接收方法返回结果。例如iadd字节码指令的作用是将两个int类型的数值相加,它要求在执行之前操作数栈的栈顶已经存在两个由前面的其他指令所放入的int类型数值。在执行iadd指令时,两个int类型数值出栈,相加求和之后求和结果重新入栈。操作数栈的每个位置上可以保存一个Java虚拟机中定义的数据类型的值,包括long和double类型。在任意时刻,操作数栈都会有一个确定的栈深度,一个long或者double类型的数据会占用两个单位的栈深度,其他数据类型则会占用一个单位的栈深度。
  动态链接
  每个栈帧内部都包含一个指向当前方法所在类型的运行时常量池的引用,以便对当前方法的代码实现动态链接。在class文件里面一个方法若要调用其他方法,或者访问成员变量,则需要通过符号引用来表示。动态链接的作用就是将这些符号引用所表示的方法转换为对实际方法的直接引用。类加载的过程中将要解析尚未被解析的符号引用,并且将对变量的访问转化为变量在程序运行时,位于存储结构中的正确偏移量。由于对其他类中的方法和变量进行了晚期绑定,所以即便那些类发生变化,也不会影响调用它们的方法。
  对象的表示
  Java虚拟机规范不强制规定对象的内部结构应该如何表示。在具体实现中,一般有两种对象的访问方式,分别是通过句柄访问对象以及通过直接指针访问对象。
  在之前的博客里我也总结过这两种对象访问方式的优劣,想要了解的同学可以参考这篇博客
  特殊方法
  在Java虚拟机层面,Java编程语言的构造器是以一个名为的特殊实例初始方法的形式出现的。这个方法名称是由编译器命名的,因为它并非一个合法的Java方法名字,不可能通过程序编码的方式实现。实例初始化方法的初始化期间,通过Java虚拟机的invokespecial指令来调用,而且只能在尚未初始化的实力上调用该指令。构造器的访问权限也会约束由该构造器所衍生出来的实例初始化方法。
  一个类或者接口最多可以包含不超过一个类或接口的初始化方法,类或接口就是通过这个方法完成初始化的。这个方法是一个不包含参数的、返回类型为void的方法,名为。
  在class文件中把其他方法命名为是没有意义的,这些方法并不是类或接口的初始化方法,它们既不能被字节码指令调用,也不会被虚拟机自己调用。当class文件的版本号不小于51.0时,方法想要成为类或接口的初始化方法,必须设置ACC_STATIC标志。
  异常
  Java虚拟机里面的异常使用Throwable或其子类的实例来表示,抛异常的本质实际上是程序控制权转移的一种即时的、非局部的转换---从异常抛出的地方转换至异常处理的地方。
  绝大多数异常的产生都是由于当前线程执行的某个操作所导致的,这种可以称为同步异常。与之相对,异步异常可以在程序执行过程中随时发生。Java虚拟机中异常的出现总是由下面三种原因之一导致的:
  athrow字节码指令被执行;
  虚拟机同步检测到程序发生了非正常的执行情况,这时异常必将紧接着发生在非正常执行情况的字节码指令之后抛出,而不会在执行程序的过程中随时抛出。例如:程序所执行的操作可能会引发异常---当字节码指令所蕴含的操作违反了Java语言的语义,如访问一个超出数组边界范围的元素,或者是当程序在加载或者连接时出现错误;还有一种异常是使用某些资源的时候产生资源限制,比如说使用了太多的内存。
  由于以下原因,导致了异步异常的发生: 调用了Thread或者ThreadGroup的stop方法;Java虚拟机实现发生了内部错误。
  当某个线程调用了stop方法时,将会影响到其他线程,或者在特定线青岛媒体邀请程组中的所有线程。因为stop方法的执行常常会导致出现数据不一致的情况,这时候其他线程中出现的异常就是异步异常,因为这些异常可能出现在线程执行过程中的任何位置。虚拟机的内部错误也被认为是一种异步异常。
  虚拟机错误
  InternalError:实现虚拟机的软件错误、底层主机系统的软件错误及硬件错误都会导致Java虚拟机出现内部错误,InternalError是一个异步异常,它可能出现在程序中的任何位置;
  OutOfMemoryError:当Java虚拟机实现耗尽了所有虚拟或物理内存,并且内存自动管理子系统无法回收到创建新对象所需的足够内存空间时,虚拟机将抛出OutOfMemoryError。
  StackOverflowError:当Java虚拟机实现耗尽了线程全部的栈空间时,虚拟机将会抛出StackOverflowError。
收费代运营
  UnKnownError:当某种错误或异常出现,但虚拟机实现又无法确定它具体是哪种异常或错误,将会抛出UnKnownError。
  由于通常虚拟机会对代码进行优化,例如指令重排。那么在异常发生的时候,有一些在异常出现位置之后的代码可能已经执行了,那这些优化过的代码必须保证它们提前执行所产生的影响对用户程序来说是不可见的。
 
深入理解Java之java虚拟机干净利落的规范总结  上

上一篇:深入理解Java之java虚拟机干净利落的规范总结 下
下一篇:32位量子虚拟机成功上线 量子“门外汉”可更快


版权声明:以上主题为“深入理解Java之java虚拟机干净利落的规范总结 上"的内容可能是本站网友自行发布,或者来至于网络。如有侵权欢迎联系我们客服QQ处理,谢谢。
相关内容
扫码咨询
    深入理解Java之java虚拟机干净利落的规范总结  上
    打开微信扫码或长按识别二维码

小提示:您应该对本页介绍的“深入理解Java之java虚拟机干净利落的规范总结 上”相关内容感兴趣,若您有相关需求欢迎拨打我们的服务热线或留言咨询,我们尽快与您联系沟通深入理解Java之java虚拟机干净利落的规范总结 上的相关事宜。

关键词:深入理解Java之java虚拟机干

关于 | 业务 | 案例 | 免责 | 隐私
客服邮箱:545321@QQ.com
电话:400-021-1330 | 客服QQ:545321
沪ICP备12034177号 | 沪公网安备31010702002418号