[ ]
【IT168技术】Valhalla项目已经成为Java社区三年多的流行语,但是关于这个重要项目的公开文章却很少。对某些人来说,Valhalla项目意味着创造价值类型的能力;对其他人来说,Valhalla项目意味着公共运行时类型的具体化。
瓦尔哈拉项目有一个非常明确的目标:阻止Java开发人员在性能和抽象之间做出选择。本文将介绍什么是Valhalla项目以及它带来的问题。
什么是瓦尔哈拉项目?
Valhalla项目是一个OpenJDK项目,始于2014年,由Brian Goetz领导,旨在为Java开发工具包(JDK)10或未来的Java版本引入基于价值的优化。这个项目的重点是允许开发人员创建和使用值类型,或者不像原语那样引用值。对Goetz说“代码像类,工作像整数”。
与引用类型(对象)相比,值类型的主要优点是在内存和计算上都消除了引用类型的开销。例如,虽然与对象相关联的实际大小和开销是特定于Java虚拟机(JVM)实现的,但是所有JVM都包含用于存储关于对象的信息的字节,包括多态信息、标识信息、同步信息和垃圾收集元数据(例如引用计数器)。此外,当必须访问与引用类型相关联的对象时,必须间接引用该引用,从而给出间接层。在某些情况下,这种开销是多余和不必要的。
虽然值类型在一些重要特性上偏离了对象,但是它们保持了类似于类的抽象级别。例如,值类型可能仍然有方法和字段,所有这些都带有便于封装的可见性修饰符。虽然在当前的Java版本中(撰写本文时是JDK 9),主要区别之一是值类型(如基本类型)不能用作泛型类型参数;例如,列表在当前版本的Java中不是有效的类型。
虽然像int这样的基元类型可以自动与引用类型(比如Integer)结合,但主要缺点是会重新引入对象的开销。尽管自JDK 5号以来,这一直是Java泛型的一个障碍,但在瓦尔哈拉项目中,开发人员可以创建新的值类型。为了解决这个问题,Valhalla还负责提供一种机制,允许值类型作为有效的泛型参数提供,同时仍然保持删除当前Java类型的一般语义。
什么是值类型?
值类型是直接存储在内存中的一组数据,而不是数据的引用(或指针)。因此,值类型可以被认为是只消耗内存来存储包含在其字段中的数据聚合,而没有额外的开销。从概念上讲,基元是值类型的一个可重新定义的例子,其中int只消耗32个字节来存储元数据。
把数据直接放入内存(而不是引用)叫做扁平化,在数组中优势更明显。在对象数组的情况下,数组中的每个元素存储对与该元素相关联的对象的引用,这要求在访问该对象之前执行取消引用。在值类型数组中,值直接放在数组中,并保存在连续内存中。如下图所示:
与引用类型相比,使用值类型有一些明显的优势,包括:
*减少内存使用:没有额外的内存用于存储对象元数据,如同步、标识和垃圾收集的标志。对于像Integer这样的小对象,对象的开销可以匹配甚至超过数据本身的大小。
*减少间接性:由于对象在Java中是作为引用类型存储的,所以每次访问对象时,都必须先取消对它的引用,导致执行额外的指令。与值类型相关联的平面化数据将立即出现在需要它们的地方,因此不需要取消引用它们。
*增加局部性:展平值对象消除了内存中间接存储的可能性,增加了内存中相邻存储的可能性,尤其是对于数组或其他连续内存结构。
引用类型和值类型的一个主要区别是标识符的对应定义:引用类型的标识符固有地绑定到对象,而值类型的标识符绑定到其当前状态。
例如,可以将日期视为值类型,其中一个值实例解析为2018年1月1日,这等于另一个值实例解析为2018年1月1日,而不管这两个实例是否相同。只要一个实例的数据与另一个实例的数据匹配,这两个实例就可以互换(也就是说,它们可以被识别为相同的值)。在实践中,日期通常被存储为从某个时间段(例如,毫秒)开始的时间单位。因此,如果两个日期值类型具有相同时间段的相同毫秒数,则它们是相等的。
值类型虽然有很多优点,但肯定也有一些缺点,尤其是在现有的Java语言中。在JDK版本的每次迭代中,最重要的问题之一是向后兼容性,主要是现有二进制字节码表示的兼容性。虽然值类型可以与Java目前使用的原始值模型相匹配,但仍然存在一些主要缺陷,包括:
*原语目前不支持有限的方法调用或字段访问(即使用点运算符);
*用户定义的值类型中没有文本;
*所有值类型都不支持内置运算符(如+);
*很难为所有用户定义类型的默认值;
除了现有原始模型中的这些缺点之外,关于用户定义的值类型还有其他一些必须解决的问题,包括:
*值类型可以实现接口吗?
*一个值可以是另一个值的子类吗?
*值类型是从基类隐式派生的吗?
*值类型是否可以在必要时视为对象?
什么是一般专业化?
自从JDK 5中包含泛型以来,Java泛型的一个主要缺陷就是缺乏对原始类型参数的支持。例如,虽然列表是有效的Java类型,但列表不是。这个缺点是Java引入泛型造成的,没有改变类文件的二进制运行时特性的要求。这将导致泛型类的类型参数被删除,并且泛型类的所有常见用途都具有相同的运行时类型。例如,虽然在编译期间处理列表和列表以确保每个类型参数都是类型安全的,但这两个类型参数将在运行时被删除并解析为相同的类型。
在运行时,解决以下问题:
这允许现有代码继续使用泛型类的原始类型,而不做任何更改,从而保持现有Java代码的向后兼容性。例如,现有代码仍然可以使用原始类型列表(没有任何泛型类型参数),因为原始类型和泛型类型的运行时类型将通过删除:列表来解析相同的类型。此外,由于泛型类型中不再存在泛型类型的参数,所有泛型参数都被引用类型对象替换。例如,给定以下类定义:
等效的运行时类型是:
然后,编译器负责生成正确的强制转换和桥接方法,以确保统一强制转换运行类型的所有使用都是类型安全的。
将有效地变成:
由于类型消除,运行时泛型参数必须解析为一个类型。选择该对象是因为它包含可以存储任何用户定义类型的最高类型。原始值(如int、double、boolean等。)不是从对象类继承的,因此禁止将其用作泛型类型参数。实际上,泛型的引入,并不能改变Java的运行时属性,导致原有的类型不能作为泛型类型参数。
如何使用值类型作为类型参数?
一般来说,有两种技术支持面向对象编程语言中的泛型。第一种技术是同构转换,它将泛型类型解析为单一类型,而不考虑它们的类型参数。这是Java使用的技术,List和List都被解析成一个运行时类型列表。第二种技术,异构翻译(或泛型专门化),在运行时解析为不同类型的单个泛型的不同类型参数。例如,列表和列表将导致类似于列表字符串和列表整数的运行时类型(与单一同类列表相反)。使用专门化有很多“副作用”,包括泛型类型层次结构的分离。
随着用户定义值类型的引入,将非引用类型用作泛型类型参数的需求变得更大。正如我们所看到的,同构转换不足以提供非引用类型的泛型参数方法。为了解决这一困境,在保持与现有的向后兼容的同时,设计了一种混合解决方案,其中同构的用作引用类型,异构的用作值类型。
为了确保现有的泛型类仍然可以运行,一个新的关键字any与泛型类型参数声明一起使用,表示它将使用增强的泛型(允许值类型和引用类型作为泛型类型参数)。例如,增强的通用Box类如下所示:
虽然在需要增强泛型时包含任何关键字看起来很麻烦,但是仍然有一些重要的情况需要维护现有的泛型语义。例如,数组列表类有以下方法:
最初,remove(int)方法被设计为假设泛型参数是引用类型。如果放宽此限制,并且允许原始泛型类型参数,将会出现方法冲突。如果常规参数t解析为int类型,上述两种方法将解析为相同的签名。这可以通过创建两个不同名称的方法来解决,比如removeByPosition和removeElement,但是仍然需要改变ArrayList的接口,所以不能假设所有泛型类都可以自动接受值类型的泛型参数。
在Valhalla项目的这一点上,关于泛型专门化还有很多问题,但是可以得出结论,如果在Java中加入值类型,那么接受这些值类型的泛型很可能会随之出现。虽然还有很多工作要做,包括带值类型的泛型语义,但还是有一些重要的决策,甚至是在初期阶段。
这是否意味着Java会被物化?
作为Java使用同类转换的必然结果,泛型类型的泛型参数在运行时不是通用的或可用的。这对于无数的应用来说是一个很大的问题,但是为了平衡向后兼容性,Java应用引入了泛型。随着Valhalla项目对泛型类型的重新审视,我们不禁要问:Java有具体的类型吗?
这个问题的答案可能是肯定的,但是只有在基于值的泛型的情况下。虽然值类型参数的通用专门化的实现还没有固化,但该实现可能包括通用类型。但是,为了保持向后兼容性,引用类型的泛型参数不太可能被视为具体的。虽然公认的泛型有许多优点,但它们也有一些主要缺点:
*与现有引用类型不兼容
*假设泛型在运行时被删除,优化失败
*字节码维护运行时类型信息的复杂性
虽然目前还没有什么是一成不变的,但是一般的具体化会进入到项目Valhalla中,即使带有值类型的泛型在某种程度上会看到部分具体化
结论
随着Jigsaw项目和函数式编程语义的引入,这是对Java语言的一种改进。Java虽然不是完美的语言,但是正在采取措施使其进一步成熟,提供新的功能和语义,从而在没有适当抽象的情况下获得更好的性能和问题映射。
1.《valhalla 你知道到底什么才是 Valhalla 项目么?》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《valhalla 你知道到底什么才是 Valhalla 项目么?》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/caijing/1587174.html