瓦尔哈拉州,4月2021年

2021-04-22 11:29:37

本文档介绍了基因类的Java虚拟机视图。注意事项不一定与Java语言视图相同;读者仍然可以在文献中携带关于Java语言的结论。

在valhalla之前,所有对象 - 类和数组的实例 - 有Aunque对象标识。 Valhalla允许类来通过标记ACC_PRimitigitive标志的类来选择,无论是他们的实例是否具有身份(" IdentityClasses")或不("原始类")。

在抽象虚拟机(如ClassFiles中描述)中,两个原始的Andidentity对象都是专门通过对象引用来引用。然而,由于JVM知道原始对象没有身份,因此它可能会优化布局(扁平化),实例化,成员访问,Andcalling对原始对象的约定(标准化)。

JVM类型描述符(JVMS 4.3.2)使用领先字母表示它们的BasicType。目前存在八种基本类型,对应于八个类型(i,j等),以及与对象法(L)对应的一个基本类型。 (字母v中出现在类型描述符中,但不得不代表一种类型。)[类型描述符中的字符引入了一个arrayType,它已硬于内部定义的阵列类,但对于我们的risptax,它是l语法的变体,引入了一种特殊类型OFOBJECT参考。

这九种基本类型对应于五种载波类型(int,long,float,double和对象引用),其对应于堆栈插槽或局部变量插槽中的值的不同表示。 (TheSetWo集之间的差异来自删除字节,短,char和boolean到INT和INTUSING I运营商。)原始运营商直接存储在墓穴或局部变量插槽中的值(长而双值使用两个相邻的插槽),以及对象参考(L)载波将对应的时隙中的对象引用存储在内。

要描述原始类型,我们添加了一个新的基本类型q,它表示对所谓的异国情调类的累积。 (目前,唯一的异国情调的类是原始类。)Q描述符具有相同的语法结构L描述符(例如,QJava / Lang / int;)。

除了重用L描述符的句法形式之外,Q DescriptorSalso还重新使用L载波 - 在JVM中,没有对原始对象的引用和对身份对象的引用没有结构差异。

Q携带和L携带的值都是由TheSame的A *字节码进行操作。然而,JVM验证器确保Q-Funddand的L携带的值,即使是同一类,也保持不同。这允许Q携带的参考参考在扁平化和标定化方面可靠地参与,优化不可能(即使原则上)对于L携带的值。

如果底层类别不包括NULL,则Q携带的值不能为NULL - 本设计中的所有原始类别的情况。这允许QType到"像int&#34这样的工作,而没有困扰JVM来表示32位整数中的空值。此外,允许在Q描述符中的允许(确实需要)的JVM比LDescriptors的情况更早地命名为Q描述符;此类早期加载允许在字段或参数的变量后立即为Q定义字段Orargument定义,而不是以后作为背面填充优化。

L VS Q描述符的选择与任何类型的描述符紧密相关联。 L描述符的合同如下:

几乎所有L描述符的使用都是不可解决的。 (因此,除了响应指定的resolution请求之外,JVM Maynot加载L描述符的类。这就是为什么Null是初始值所需的原因。)

如果通过L描述符链接,则数据结构可以是圆形的。 (这种不分辨的L描述符的ISANORCORCORARY。)

由于LDESCRIPTERS,应用程序不会遭受引导循环。 (另一个不分辨的L描述符的推论。)

如果参考操作数为NULL,则校准校准的正常执行旨在处理其类型的操作数如果是L描述符。 (在这种情况下没有解决。)

Q描述符的合同本质上是一个匹配的"反合同" l l描述符合同:

几乎所有Q描述符的使用都会热切地解决,在首次创建该类型的字段之前,或者首先准备使用该类型(asargument或返回值)的方法。 (因此,在许多情况下,JVM必须加载Q描述符的字段。这是如何排除NULL的值,以及如何排列呼叫序列的扁平字段和标定方法。)

如果通过Q描述符链接,则数据结构可能不是圆形的。(相反,JVM可以在单个内存块中嵌套Q携带的值。)

应用程序可能因QDescriptors而遭受引导循环。 (过度使用Q描述符在类装载中携带InfiniteRegress的风险。)

应用于Q描述符时执行触摸侦查(而不是A类名称)执行急切分辨率。

多字Q字段和数组元素可以展示" Struct Teating"在过程中,除非采取其他措施来防止这种情况。

Q描述符名称不需要存储类加载器约束。(由于急于加载,可以使用zeratfvedClass在编写方法时检查约束。这是类LoaderConstrain检查的变体,不会在SystemDictionary中存储约束记录。)

有了这一点,似乎任何阶级都可能以某种阶级可能以某种阶级提供从存储在Q描述符中的值以及l描述符。那不是这种情况,而且JVM禁止"轻浮" Q描述符Tonon-Exotic类,检查时,只要解决Q携带的类名,实际上都是引用异国情调的课程。 (在目前的设计中,其中具有原始类的特征,Q描述符只能应用于PrimitiveClass。可能会使用Q描述符的未来使用,这是利用Qfactract来支持其他种类的异国情调类型,例如专门的泛型族族类型。)

由于它们的稳健性和有用性,L描述符可以被添加到任何类型的类,并且确实使用primitiveClasses执行许多有用的作业,因为在拒绝或循环或速度或延迟装载是必要的情况下。但由于L描述符是懒惰的,因此无法解除基本类的L描述符可以描述该类' s属性,足够的精度(和及时性)来允许L描述符的所有用途以连续变平和标定。这就是为什么Valhalla需要一个"抗L"不可能简单地调整L描述符的行为来解锁womeOltimizations,因为所需的急切加载违反了L描述符的一些基本aschoperies。

(如果Q描述符快速加载,则可以轻松回忆Q和L描述符的角色,而L描述符直到以后延迟。L的真实起源不清楚;也许它是它的第二个字母"班级"自从C c是拍摄的。选择字母q以指的是a"数量"在设计期间。Q也代表" quid&#34 ;,拉丁文"什么?" SOIT也被注意到急切分辨率的Q描述符号" Quiddity"通过快速加载其类文件,该类型的类型。)

原始类可以实现接口,并且可以扩展某些restrictedAbstract类以及特殊类对象。这意味着可分解的接口类型,合适的抽象类类型或对象可以基于身份对象或基元对象的引用。 JVM处理SUCH扩展为普通亚型,因此对原始对象的引用可以束缚,以引用对象或合适的接口或抽象类打字铸造。原始类没有构造方法;相反,使用Sameargument List作为声明的构造函数将源级别构造函数转换为静态工厂方法,方法名称< new&gt ;.与< init&gt不同;方法必须返回void,静态出厂方法必须返回构造的类的Q描述符类型。 (静态FactoryMethod命名为< new>也允许返回对象,或者也许是该类的Theupertypes之一,作为L描述符;这是Exotichiddens的必要条件。)

无需任何异国情调(即,原始)类的超型以任何方式都是异国情调。因此,虽然q(有时l)描述符与原始值播放角色,但与upsupertypes相关联的描述符永远不会是q描述符。特别是,对于qjava / lang /物体没有法律用途;

一个抽象类是一个原始的超类候选者,如果它没有字段,它的arg构造函数是空的(标记的Acc_abstract),它没有其他< init>方法,它的超类是对象或基本的超级类andidate。 (原始类的静态工厂不呼叫TheuperClass构造函数。如果构造函数是ExtentAnyway,则才能安全。)

作为一种原始超类候选的属性是"传染性"上衣链。也就是说,如果有些抽象类具有空的无argconstructor,则其直接超类也必须具有空的无argconstructor。似乎介绍甚至对象必须具有空(ACC_ABSTRACT)构造函数,但对象是特殊的,因为允许其单独(allclasses)具有真正的空混凝土构造方法。因此,对象是免于" contagion" extenconstructors的要求。

作为一种"荣誉界面",对象既不是陈旧的课程;它既不实现标记接口IDENTITYOBJECT和PrimiventObject。然而,表达式新对象()的现有行为创建了一个身份对象。似乎对象戴着太多的帽子!JVM必须为字节码指令新的Java / Lang /对象提供特殊处理,其中链接分辨率将创建一个没有字段的秘密替代类的身份对象(如对象)并实现IdentityObject。验证者不必知道此问题,并对象中断的invokespecial ::< init>将是唯一初始化对象的经历。本秘密课程将被设计为允许Thistreatment。

如果身份类别声明了一个原始的超类候选者作为其超级,Itmust(像往常一样)呼叫超类< init>方法,但JVM将此CallStraight链接到对象的No-Arg构造函数,跳过interventingabstract(空)< init>方法。

如果允许原始的超类候选者具有任何字段ornon-empty< init>方法,它们可能以通常的方式可用超类的身份子类。但那些额外的< init>方法可以通过同一超类的原始子类,即使是某种方式是某种方式"拉下来"进入原始类,他们不会被它初始化。如果可以以某种方式围绕这些障碍,可以创建java.lang.enum和其他重要类的原始子类。当我们有更多的经验时,我们可能会重新审视并放松原始的限制。

与他们的IdentityCounterParts相比,原始类具有一些限制。原始类的实例字段必须标记为arc_final,并且不得标记为arc_volatile。

必要时,JVM隐式调整原始类,以实现接口PrimiventObject。它们或其超级器可以显式将primiventObject视为超级,在这种情况下,JVM不会插入extraSupl。它们可能不会直接执行界面IDENTITYOBJECT。

此外,由于必要的是,通过JVM根据需要隐式调整,因此无法通过JVM暗中调整intercityObject,因此无需成为原始超类候选的抽象类(由于它们包含字段)。它们可能无法直接地执行界面primiventObject。

通过这种方式,每个具体对象都恰好实现了IdentityObject或PrimiventObject中的一个,并且每个抽象类实现在最多的两个界面和对象中的一个。

通过Class :: GetInterfaces的反射,JVM插入的超级器不可见,但在每个实例基础上,通过CharcCast,Instanceof,Aastore,Class :: Isinstance和Class :: Cast vercapect。它们也被类别:: IsAssignableFrom尊重。因此,要检测JVM是否已将Super IdendityObject插入某些类C类,首次呼叫类:: iSassignableFrom for IdentityObject的每个超级C,以及如果它不存在,则在C本身上测试类:: isassignablefrom;如果C神秘地实现了接口,尽管没有其取代,但JVM已经暗中制作了Itsmark在C上的Itsmark。保持秘密从反射中是一种危险的业务,但在这个危险之中,似乎更安全,因此似乎更加安全,即大量现有的Java Classessuddenly成长他们的类中的新项目:: getInterfaces阵列。

如果原始类扩展到类,则该类必须是一个原始的超类候选者。原始类foo的theInstance字段可能不会直接orindirect,使用q描述符具有类型foo的字段。 (这样的物体没有明确的尺寸;每个人都包含相同类型的无限回归。)

在任何情况下,可以将L描述符应用于基本类;任何INFINITERERGER立即通过NULL引用停止。

FOO中使用Q描述符的静态字段是NORPROBEMBITATIC,但它需要仔细准备,并且(一般)隐藏indigirection来管理延迟初始化。类型QFoo类型的静态字段;必须初始化为foo的默认值,但在默认值的实现之前,jvm可能需要toprepare这样的静态字段,这可能需要懒惰或间接访问该字段。如果是这样,则此InterSallogic可以隐藏在GetStatic字节码内。

原始类的融合影响了许多字节码。一些竞队,其他人在应用于PrimitiveTypes时获得额外的行为或限制。现有的a *字节码扩展以统一支持Referencesto既标识和原始类。

defaultValue是基本类实例新的新模拟;它会引用intack上的原始类的默认(全零)实例。 (Q类型的字段和数组元素也被初始化为该类型的TheDefault值,就像DefaultValue一样。)该字节码的唯一操作数是对常用_Clands项目的引用,它给出了基本类(不是其Q描述符)的名称。

withfield是putfield的基本类实例的模拟; Ittakes对堆栈上的基本对象的引用和新的字段值,andleaves堆栈上的引用,以字段与原始实例的基本类的实例,除了指定的域外。

与菲尔德指示受限制;它只能在Classthat中执行,声明正在修改的基本类。这封装了NOVEL值的创建,因此该类可以确保任何逃避SIMPETION的任何值,所看到的符合其不变性。 New和Putfield Bytecodes Maynot与原始类的实例一起使用,并且与Identity类一起使用WITField Bytecode Maynot。

(在VM的未来版本中,一个类可能能够在退出基础上授予对其他客户端的访问权限,如果这似乎有用。Infact,在未来的JVM中,A类也可能会撤销访问在其他客户端中的DefaultValue指令,在退出的基础上,如果这是有用的,则再次出发。)

AseStore指令对元素值执行null检查,将值置于一个原始类实例数组中。

ACMP *指令在obsoperand之间执行更复杂的比较。如果它们都是NULL,则两个对象引用是相等的,也是对相同标识对象的引用,或者对其相同类型的引用和所有字段的引用且所有字段的引用;等价物和#34;同步仪表递归应用于与字段的合适的平等比较,这对原语进行普通的平等(浮动和双字段除外,与浮点数::等于和Double ::等于的语义)和ACMP进行参考。 IF_ACMPNULL指令始终会产生对原始对象引用的错误。

COCCHACT指令扩展到应用于Q描述符,具有以下内容校验象,以其遵循L描述符的规则。不纯,校准的正常行为是值得超薄的堆叠空,而无需加载该类。 (这是一个不寻常的普通常量_class项目的情况;它将常量池属于最多,就像它是l描述符一样。)但是常用_class项现在可以包含q描述符以及两个现有选择:classNames和Array描述符。 (可以看出,这三种名称是串联不相交的。)应用于Q描述符的校验符始终是它的Q描述符,并且将根据类文件中找到的要求处理任何空的存在(目前将始终拒绝a空值)。

ANEWARRAY指令类似地扩展到应用于Q描述符;这决定了结果数组是否扁平化(Q)或不是(L)。注意,ANEWARRAY指令返回对ANARRAY的普通(l)引用,无论其组件类型如何。

Aaload指令一如既往地占用特定参与类型的数组,并返回该类型的元素。如果数组元素类型ISA Q类型,则Veriferas A Q类型跟踪Aaload在堆栈上推动的值。始终如一地,Astore指令完全依赖于历史记录阵列存储检查;验证者对Astore的阵列输入的精确组件类型漠不关心。

应用于包含Qdescriptor的常数_class项的LDC指令将返回类中的辅助(val)镜像。 normalsyntax(常用_class项中的普通类名)将返回其上身(ref)镜像;同样,它好像正常的语法就像LDescriptor一样。

大多数常量_class的用途继续要求普通类名,而不是alraydescriptors或q描述符。指定Qdescriptor的常数项目仅对以下使用有效:

特别地,constant_fieldRef,constant_methodref和constant_interfacemethodref项目和instanceof指令不得将常数_class项与q描述符引用。

验证者跟踪"风味"每个参考值(l vs q)在其源处的描述符中而异。由于LDEScriptors的合同是从Qdescriptors的各种方式更宽松(对于值集),因此验证者的行为就像QFoo一样;是Plainpreference foo的子类型(也称为lfoo;),每个foo。

验证者在将其发送到以下消费者时,Q Q标记值:

在某些情况下,JVM使用动态检查来确保类型安全,并且助手不强制对这些消费者的Q类型的标记,但是禁止接受Q和L标记的类型:

Getfield,Putfield和Invoke家族的接收者(这些已经包括强制空白检查)

没有必要 ......