简体   繁体   English

新型 Java 17 型模式匹配开关如何在引擎盖下工作?

[英]How does the new Java 17 type pattern matching switch works under the hood?

How does the new Java 17 type pattern matching switch works under the hood?新型 Java 17 型模式匹配开关如何在引擎盖下工作? As the feature is fairly new, this question doesn't talk about it.由于该功能相当新, 所以这个问题没有谈论它。

Reminder: for this code to work under Java 17, you need to enable preview features提醒:要在 Java 17 下运行此代码,您需要启用预览功能

public static void test (Object o) {
  System.out.println(switch(o){
    case Number n -> "number " + n;
    case Enum e -> "enum " + e;
    case String s -> "string " + s;
    default -> "other " + o;
  });
}

Disassembled version of the code above using javap -c :上面使用javap -c的反汇编版本的代码:

   0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
   3: aload_0
   4: dup
   5: invokestatic  #13                 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
   8: pop
   9: astore_1
  10: iconst_0
  11: istore_2
  12: aload_1
  13: iload_2
  14: invokedynamic #19,  0             // InvokeDynamic #0:typeSwitch:(Ljava/lang/Object;I)I
  19: tableswitch   { // 0 to 2
                 0: 44
                 1: 58
                 2: 74
           default: 90
      }
  44: aload_1
  45: checkcast     #23                 // class java/lang/Number
  48: astore_3
  49: aload_3
  50: invokedynamic #25,  0             // InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/Number;)Ljava/lang/String;
  55: goto          96
  58: aload_1
  59: checkcast     #29                 // class java/lang/Enum
  62: astore        4
  64: aload         4
  66: invokedynamic #31,  0             // InvokeDynamic #2:makeConcatWithConstants:(Ljava/lang/Enum;)Ljava/lang/String;
  71: goto          96
  74: aload_1
  75: checkcast     #34                 // class java/lang/String
  78: astore        5
  80: aload         5
  82: invokedynamic #36,  0             // InvokeDynamic #3:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
  87: goto          96
  90: aload_0
  91: invokedynamic #39,  0             // InvokeDynamic #4:makeConcatWithConstants:(Ljava/lang/Object;)Ljava/lang/String;
  96: invokevirtual #42                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  99: return

Given this key line:鉴于这条关键线:

  14: invokedynamic #19,  0             // InvokeDynamic #0:typeSwitch:(Ljava/lang/Object;I)I

It looks like Java converts the object into an int in an auto-generated int typeSwitch (Object, int) method, of which I can't see the generated code.看起来 Java 在自动生成的int typeSwitch (Object, int)方法中将 object 转换为 int,我看不到生成的代码。 The goal of this conversion is then to be able to use a regular switch table over int values.然后,此转换的目标是能够对 int 值使用常规开关表。

At first, I thought that hashCode of was used, ie object.getClass().hashCode() , as it is done with switch over String, but in fact Class object's hash code isn't constant over consecutive runs of the JVM. At first, I thought that hashCode of was used, ie object.getClass().hashCode() , as it is done with switch over String, but in fact Class object's hash code isn't constant over consecutive runs of the JVM. So it's impossible unless the mapping table is rebuilt at every execution, what I doub about.因此,除非在每次执行时都重建映射表,否则这是不可能的,我对此表示怀疑。

So, the questions are:所以,问题是:

What does this typeSwitch method do?这个 typeSwitch 方法是做什么的?

Is it smarter than a chain of if instanceof else if instanceof?它比 if instanceof else if instanceof 的链更智能吗? Is that possible?那可能吗?

What's the purpose of the additional int passed?传递的额外 int 的目的是什么?

The invokeDynamic instruction (upon first being hit) calls the SwitchBootstraps.typeSwitch() method. invokeDynamic指令(在第一次被命中时)调用SwitchBootstraps.typeSwitch()方法。

The last argument of the SwitchBootstraps.typeSwitch() method (the labels parameter) is in this case the list of classes in the switch: Number.class , Enum.class , String.class SwitchBootstraps.typeSwitch()方法的最后一个参数( labels参数)在这种情况下是开关中的类列表: Number.classEnum.classString.class

The SwitchBootstraps.typeSwitch() bootstrap method checks the labels parameter for correctness and then returns a ConstantCallSite for the SwitchBootstraps.doTypeSwitch() method that does the effective handling (ie the final execution of the invokeDynamic instruction). SwitchBootstraps.typeSwitch()引导方法检查labels参数的正确性,然后为进行有效处理(即invokeDynamic指令的最终执行)的SwitchBootstraps.doTypeSwitch()方法返回ConstantCallSite

If you look at what SwitchBootstraps.doTypeSwitch() does: it iterates over the list of classes and returns the first found match (with some provision of restarting the iteration, I don't know why this is needed).如果你看看SwitchBootstraps.doTypeSwitch()做了什么:它遍历类列表并返回第一个找到的匹配项(通过一些重新启动迭代的规定,我不知道为什么需要这样做)。

What does this typeSwitch method do?这个 typeSwitch 方法是做什么的?

It takes an Object , figures out its real type, then maps that to an integer which represents the label index.它需要一个Object ,找出它的真实类型,然后将其映射到一个代表 label 索引的 integer 。 The integer is then used for the following conventional tableswitch instruction that you see.然后将 integer 用于您看到的以下常规tableswitch指令。

This is implemented in an internal class called java.lang.runtime.SwitchBootstraps by calling the typeSwitch method.这是通过调用typeSwitch方法在名为java.lang.runtime.SwitchBootstraps的内部 class 中实现的。 The method is passed an additional Object[] which will contain the Class object denoting the type of each of the case labels of the type switch.该方法传递了一个额外的Object[] ,它将包含Class object 表示类型开关的每个案例标签的类型。

Is it smarter than a chain of if instanceof else if instanceof?它比 if instanceof else if instanceof 的链更智能吗?

The SwitchBootstraps class uses a for loop that tests the alternatives using Class.isAssignableFrom . SwitchBootstraps class 使用for循环,该循环使用Class.isAssignableFrom测试替代方案。

The comment above that code says:该代码上方的注释说:

// Dumbest possible strategy

Is that possible?那可能吗?

I don't know.我不知道。 Possibly.可能。

It is also possible that the JIT compiler will do something clever when compiling a call to typeSwitch . JIT 编译器在编译对typeSwitch的调用时也可能会做一些聪明的事情。 If the SwitchBootstraps code is only called when interpreting bytecodes, the "dumb" approach is... just fine.如果仅在解释字节码时调用SwitchBootstraps代码,那么“愚蠢”的方法......就好了。

It is also possible that they intend to put some smarts into the JIT compiler in a future release.他们也可能打算在未来的版本中将一些智能功能放入 JIT 编译器中。

What's the purpose of the additional int passed?传递的额外int的目的是什么?

I'm not sure.我不确定。 It determines where the typeswitch method starts searching in the Objects[] that represents the labels.它确定typeswitch方法在表示标签的Objects[]中开始搜索的位置。

Looking at the code, the for loop I mentioned above also copes with numbers, characters and strings in the labels array.查看代码,我上面提到的for循环也处理标签数组中的数字、字符和字符串。 So I suspect that this same code is used (or intended to be used) for classic and string switch statements.所以我怀疑相同的代码用于(或打算用于)经典和字符串 switch 语句。 If that is the case, I suspect that the additional int is to provide an alternative way to implement switch case drop-through.如果是这种情况,我怀疑额外的int是提供一种替代方法来实现 switch case drop-through。


If you want more detailed information, I recommend that you do what I did.如果您想要更详细的信息,我建议您按照我的做法进行操作。 Trawl through the OpenJDK source code for Java 17.浏览 Java 17 的 OpenJDK 源代码。

To use your metaphor: look under the hood yourself!用你的比喻:看看你自己!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM