谁调用 describeConstable(),何时调用?

Lai*_*son 10 java constants

我有点高兴地偶然发现Constable了 JDK 15 之类的东西。我基本明白了。

在翻遍了所有编译器理论甚至理解了一点之后,我发现我仍然有一个问题:谁调用 aConstabledescribeConstable()方法,何时调用? Brian 的演示似乎暗示它在编译时以某种方式被访问。对这些事情太天真了,我期待它出现在使用页面jdk.compiler或其他东西中。相反,唯一的消耗似乎是在jdk.incubator.foreign包中。(显然,我知道它可能会被使用页面未公开的某些私人机器使用;因此我的问题。)

我放在Thread.dumpStack()一个describeConstable()哑类的实现,实现Constable和回报Optional.ofNullable(null)只是为了看看会发生什么,......什么都没有发生在编译期或运行时。

(我确实知道,在 JDK 12 之前,如果您想编写动态常量,您必须使用 ASM 或 ByteBuddy 或类似的东西。不过,在我幼稚的眼中,它看起来像是Constable允许您的用户类“插入”Java编译器并允许为你做不断的写作。我也知道里面的类和方法java.lang.constant主要是为编译器编写者准备的,但Constable在我看来有点例外。最后,我显然明白可以调用我希望随时使用这种方法,但这显然不是它的目的。)

编辑:(非常)感谢下面一些非常有帮助和耐心的答案和评论,我想我开始明白了(我不是编译器,这点应该很明显)。虽然我知道一旦一个X implements Constableexists的实例,那么ContantDesc它从它的返回describeConstable()必须由其他常量描述符(本身)组成,虽然我知道ClassDesc#of()可以在编译时调用“常量工厂”(例如等等),显然必须只接受其他常量作为他们可能需要的任何参数,我仍然不清楚 X implements Constable首先在编译期间如何实例化任意对象,而......它正在被编译(!)describeConstable() 可以在编译时调用它。

请记住,这个问题的答案可能是我所缺少的关于编译器的基本信息,或者他们在静态分析期间遇到的各种问题。我只看到一个实例方法 ( describeConstable()),它需要在对象 ( X implements Constable)的实例上调用,并且为了拥有对象的实例,必须有人调用其构造函数。我不清楚 Java 编译器如何知道如何X implements Constable使用其任意的、可能是多参数的构造函数构造 my以便它可以调用describeConstable()它。

Eug*_*ene 7

我会说到目前为止我所理解和知道的。这确实是一个有趣的功能。

谁调用了 Constable 的 describeConstable()

javac 将要。

什么时候?

当它第一次被调用/需要时。

更详细的解释。你知道 lambdas 是如何编译的吗?如果没有,这里是非常简短的介绍(稍后会很有帮助):

Runnable r = () -> {System.out.println("easy, peasy");};
r.run();
Run Code Online (Sandbox Code Playgroud)

如果您查看字节码,将会有一个invokedynamic调用:

invokedynamic #7,  0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
Run Code Online (Sandbox Code Playgroud)

反过来,这将调用“引导程序”方法:

BootstrapMethods:
 0: #39 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Run Code Online (Sandbox Code Playgroud)
  • 引导方法的名称是:LambdaMetafactory::metafactory

  • 作为输入,它需要一个Lookup(由 JVM 提供)

  • 除此之外,javac提供了一个MethodType(它描述了方法的返回类型和方法参数类型,在这种情况下它是runfrom Runnable

  • 它将返回 a CallSite(在这种情况下它实际上是 a ConstantCallSite)。

因此,用相当简单的话说(并且很可能有点错误),invokedynamic将调用绑定到 a ConstantCallSite,它在内部将调用委托给使用您提供Runnablerun方法的实现(在内部它委托给“去糖化”私有定义 lambda 的方法)。这只会发生一次,在第一次调用时,所有后续调用都不会经历这种痛苦。不知何故,我在其他答案中提供了更多详细信息,例如此处

相同的机制将用于动态常量(但它必须使用ldc而不是invokedynamic)。jdk-11 中已经提供了“机器” 。注意类的名称 : ConstantBootstraps,我们知道为什么是“bootstrap”,我们知道为什么是“Constant”。如果你看一下这些参数,它肯定会开始变得有意义,因为它真的很像invokedynamicfor lambdas。


现在您知道为什么Constable/ConstantDesc需要了:以便引导方法调用正确的实现。在上述情况下,javac“知道”(推断/推断/等)lambda 确实是一个Runnable. 在“恒定动态”的情况下,该信息将通过类实现的事实隐含Constable。这将是如何构建常量的“配方”;至少在我的理解中。


请注意,其他人已经在JVMScala 的lazy. 但他们只是简单地在幕后实施双重检查锁定volatile,有时您需要为阅读付费……当然,在幕后实施这一点JVM是有益的;到什么程度,究竟如何尚未可知; 因为这在javac尚未实现,至少在主流 jdk 中没有实现。可能是这样的:

// made-up syntax
__@lazy__
private static final MyObject obj = null;
Run Code Online (Sandbox Code Playgroud)

这最终将委托给Constable::describeConstable或可能是:

__@lazy(provider="myProvider")__
private static final MyObject obj = null;

private MyObject myProvider(){....}
Run Code Online (Sandbox Code Playgroud)

但我敢打赌,比我聪明得多的人会想出我在这里没有提到的如何使用它的想法。当这种情况发生时(我知道它会发生),我需要更新这篇文章。

  • @LairdNelson,这正是[您自己链接的演讲](https://www.youtube.com/watch?v=iSEjlLFCS3E&t=17m14s)中所说的,需要在编译时执行某些方法的能力。从技术上讲,“弄清楚字节码……”的作用与仅执行字节码以获得结果之间没有区别。我真正喜欢这个演讲的地方是它如何将一切描述得直截了当(甚至已经完成),但到目前为止我们得到的只是一堆没有实际预期有用功能的界面。但我想,这使它成为一个典型的 Java API。 (2认同)
  • 这正是我所说的。正如演讲中所描述的,常量的组成必须由“可内在化的常量表达式”组成。正如演讲中所说,这条路径不需要与随后返回的“describeConstable()”相同。这与编译时间常量的工作方式没有太大不同。我可以写,`final String s="C"+(int)Math.PI/Thread.NORM_PRIORITY+"DE";`,它将是一个编译时常量,因为所有输入都是常量,但结果值在常量池不会反映这种结构,而是简单的四字母字符串。 (2认同)
  • 好的; 我在“Constable”的javadoc中没有看到任何关于谁可以实现它的限制,即一个在其构造函数中连接到数据库的对象,并获取一个值来构造一个“DynamicConstDesc”,然后由根据接口的约定,“getConstable()”似乎是完全合法的。我在“Constable”文档中没有看到任何对跟踪常量或 ICE 或任何内容的引用。你是说一定有,而它的遗漏是一个严重的文档错误吗? (2认同)