跳到主要内容

JEP 215:javac 的分层归因

概括

实施新的方法类型检查策略,以javac加快参数位置中多表达式的归属。

目标

实施一种新的方法来检查多表达式的类型,即_分层归因_(TA),它提供:

  • 通过实施减少归因给定表达式所需的(冗余)传递数量的方法来提高性能,以及

  • 与当前类型检查实现的结果相同。

非目标

改变可编译程序的空间并不是目标,尽管修复现有类型检查方案中潜伏的错误可能会导致一些变化。编译器生成的消息中也会出现一些细微的差异,这是可以预料的。

显着提高当前代码的整洁性和可维护性也不是一个目标 - 尽管此 JEP 引入的一些更改也可能会导致更干净的代码。

动机

目前实现 Java SE 8 多表达式类型检查的方法称为“推测归因”(SA); SA的主要思想是针对不同的目标对同一棵树进行多次类型检查;这使得可以根据多个重载解析目标检查 lambda 表达式。

在重载解析过程中执行类型检查的能力是一种非常强大且灵活的技术,但它在性能方面的代价非常高。更准确地说,使用 N 个重载候选者,可以检查相同的参数表达式最多 N * 3(每个重载阶段一次,严格、宽松、可变参数)+ 1(最终检查阶段)。如果参数表达式允许嵌套(例如返回多方法调用的 lambda),则需要将这些因素相乘,从而导致归因调用的组合爆炸。

对推测归因机制的指数级调用导致了性能问题,这些问题已被观察到并报告为错误,例如JDK-8077247JDK-8078093JDK-8055984

描述

此 JEP 提出了一种替代且更有效的实现方案,用于支持javac.从概念上讲,在执行重载决策时不需要对表达式进行类型检查;事实上,参数表达式可以以自下而上的方式进行类型检查 - 导致无归因的重载检查,或者此类表达式与适用性无关(请参阅 JLS 15.12.2.2) - 意味着此类表达式不会做出贡献进行重载解析适用性检查。例如,lambda 表达式可以是显式的 - 在这种情况下,可以在重载解析之前执行主体的类型检查;或者它可以是隐式的,在这种情况下,在重载解析期间不需要类型检查。

分层归因背后的主要思想是在重载解析之前生成自下而上的结构类型(给定方法调用中出现的每个多参数表达式一个)以及执行重载解析适用性检查所需的所有信息 - 即无需进一步需要归因。此类结构类型可能包含部分推断的类型变量,这些变量仅在稍后阶段(在调用类型推断期间)进行修复(请参阅 JLS 18.5.2)。将为以下参数表达式创建新的结构类型:

  • 拉姆达表达式,
  • 条件多元表达式,
  • 通用方法调用,
  • 带括号的多重表达式,
  • 方法参考,以及
  • 钻石实例创建表达式。

由于上面提到的一些表达式可以允许嵌套,因此结构类型可以提及其他结构类型。例如,返回泛型方法调用的 lambda 表达式的情况可以使用指向对泛型方法调用进行建模的另一个结构类型的结构类型(对于 lambda 表达式)进行建模。在这种情况下,与结构类型相关联的重载检查可以是递归的——例如,lambda 主体中的返回表达式提到的所有结构类型都必须对照重载解析机制提供的目标类型进行检查。

结构类型通常在归属方法/构造函数调用的参数时创建。在方法/构造函数调用之外的上下文中出现的 Poly 表达式应该以与现在相同的方式处理。

为了适当地实现嵌套泛型方法调用,还需要对类型推断进行一些更改;更具体地说,类型推断机制必须配备更好的保存和回滚功能,以便处理涉及泛型方法调用的重载检查(这可能需要执行一些推断任务),而不会永久污染与此类调用相关的上下文信息。

测试

javac已经包含一套全面的回归测试,以证明 SA 按预期工作;虽然大多数这些测试主要是黑盒测试,但它们应该能够有效地捕获分层归因实施错误。由于此 JEP,可能会添加一些额外的测试 - 特别是与众所周知的性能瓶颈相关的测试。

依赖关系

这里提出的工作可能会使两个相关的 JEP 受益:正确处理导入语句 (JEP 216)注释管道 2.0 (JEP 217)