This is the eighth article in the In-depth Learning ABOUT the JVM series

preface

There are five types of call-related instructions in Java bytecode.

  1. Invokestatic: used to invokestatic methods.
  2. Invokespecial: Used to call private instance methods, constructors, and instance methods or constructors of the parent class using the super keyword, and the default method of the interface implemented.
  3. Invokevirtual: Used to invoke non-private instance methods.
  4. Invokeinterface: used to invokeinterface methods.
  5. Invokedynamic: used to invokedynamic methods.

Invokestatic and Invokespecial are statically bound, while Invokevirtual and InvokeInterface are dynamically bound.

The first four instructions, described in previous articles, are bundled with symbolic references containing the target method class name, method name, and method descriptor. Before actually running, the Java virtual machine will link to the specific target method based on this symbolic reference.

Let’s show an example. Animals all run differently. How can we call their race methods in the same way?

class Horse {
  public void race(a) {
    System.out.println("Horse.race()"); }}class Deer {
  public void race(a) {
    System.out.println("Deer.race()"); }}class Cobra {
  public void race(a) {
		System.out.println("Cobra.race()"); }}Copy the code

As requested, we have two solutions: abstract the race() method or use an adapter; The second is the use of reflex mechanisms.

Just a quick demonstration.

public interface Animal {
  void race(a);
}

public class Horse implements Animal {

  @Override
  public void race(a) {
    System.out.println("Horse.race()"); }}...// Use interface abstraction methods
  public static void main(String[] args) throws Exception {
    Animal animal = new Horse();
    animal.race();
  }
// reflection calls
  public static void main(String[] args) throws Exception {
    Horse object = new Horse();
    Method method = object.getClass().getMethod("race");
    method.invoke(object);
  }
Copy the code

When these two schemes are converted into bytecode files, it can be found that they are still related to the four instructions, requiring the class name, method name and method descriptor to complete the method call. Is there any other way to achieve this?

Java 7 introduces a new instruction, Invokedynamic. The invocation mechanism of this directive abstracts the concept of call points and allows applications to link call points to any qualified method. A new concept was introduced for this purpose: a MethodHandle.

Note: Unless otherwise noted, this code is executed based on JDK8.

The concept of a method handle

The method handle is a strongly typed reference that can be executed directly. This reference can point to a regular static or instance method, or to a constructor or field. When pointing to a field, the method handle actually points to the dummy method that contains the field’s access to the bytecode, semantically equivalent to the getter or setter method for the target field.

The type of a method handle (MethodType) consists of the parameter type of the method to which it points, as well as the return type. It is the only key used to verify that the method handle is suitable. When using a method handle, we don’t really care about the class or method name of the method the handle points to.

create

MethodHandles are created using the methodhandles.lookup class. It provides multiple apis that can be looked up using Method in the reflection API or by class, Method name, and Method handle type.

MethodHandle is created as follows:

Method 1: Create a MethodHandle with reflection

public class Cobra {
  public static void race(a) {}public static Lookup lookup(a) {
    return MethodHandles.lookup();
  }
}

MethodHandles.Lookup lookup = Cobra.lookup();
Method method = Cobra.class.getDeclaredMethod("race");
MethodHandle methodHandle = lookup.unreflect(method);
Copy the code

Method 2. Create a MethodHandle based on MethodType

public class Cobra {
  public static void race(a) {}public void say(a){
    System.out.println("say");
  }

  public static Lookup lookup(a) {
    returnMethodHandles.lookup(); }}public class RaceTest {

  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = Cobra.lookup();
    MethodType methodType = MethodType.methodType(void.class);
    MethodHandle methodHandle = lookup.findStatic(Cobra.class, "race", methodType);

    MethodHandle methodHandle2 = lookup.findVirtual(Cobra.class, "say", methodType); }}Copy the code

From the above demonstration, we found that when using method 2, the user needs to distinguish between specific call types. For example, for static methods called with invokestatic, we need to use look.findstatic. For instance methods called with Invokevirtual and interface methods called with InvokeInterface, we need to use the findVirtual method; For instance methods called with Invokespecial, we need to use the findSpecial method.

Several MethodHandle methods correspond to bytecode:

Call method handle, and the original corresponding call instruction is consistent. That is, it also uses dynamic binding for method handles that were originally called by Invokevirtual; For the method handle originally called with Invokespecial, it is statically bound.

permissions

Method handles also have permission issues. Unlike the reflection API, however, permission checking is done during handle creation.

The Java virtual machine does not check the permissions of method handles during the actual call. If the handle is called multiple times, it saves the overhead of repeated permission checks compared to reflection calls. Note that access to the method handle does not depend on where the method handle was created, but on where the Lookup object was created.

As shown in the following example, we are in the RaceTest class trying to access the private methods of the Cobra class and are prompted with a permission error.

public class Cobra {
  private void eat(a){
    System.out.println("eat"); }}public class RaceTest {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodType methodType = MethodType.methodType(void.class);
    MethodHandle methodHandle = lookup.findSpecial(Cobra.class, "eat", methodType,Cobra.class); }}Copy the code

An error message is displayed when the preceding code is executed:

Exception in thread "main" java.lang.IllegalAccessException: no private access for invokespecial: class com.msdn.java.hotspot.invokedynamic.Cobra, from com.msdn.java.hotspot.invokedynamic.RaceTest
Copy the code

This code is for private methods, so let’s try to manipulate private fields. For a private field, if the Lookup object is retrieved from the private field’s class, the Lookup object has access to that private field. But in other classes, it is logically possible to create a getter or setter for that private field from the Lookup object, specifically through the findGetter and findSetter methods of MethodHandles.Lookup.

First let’s test creating Lookup objects in other classes to see if we can manipulate private fields.

public class Cobra {

  private String name;
}

public class RaceTest {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodHandle methodHandle = lookup.findSetter(Cobra.class, "name", String.class);
    Cobra cobra = new Cobra();
    methodHandle.invokeExact(cobra,"111"); }}Copy the code

The above code execution error:

Exception in thread "main" java.lang.IllegalAccessException: member is private: com.msdn.java.hotspot.invokedynamic.Cobra.name/java.lang.String/putField, from com.msdn.java.hotspot.invokedynamic.RaceTest
Copy the code

If the Lookup object is created in the Cobra class, can we manipulate private fields?

public class Cobra {

  private String name;

  public static Lookup lookup(a) {
    returnMethodHandles.lookup(); }}public class RaceTest {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = Cobra.lookup();
    MethodHandle methodHandle = lookup.findSetter(Cobra.class, "name", String.class);
    Cobra cobra = new Cobra();
    methodHandle.invokeExact(cobra,"111");
    MethodHandle getterMethodHandle = lookup.findGetter(Cobra.class, "name", String.class); System.out.println(getterMethodHandle.invoke(cobra)); }}Copy the code

Manipulation of private fields in an external class

Because of permissions, Lookup objects created in external classes cannot manipulate private fields, so what’s the alternative?

Option 1: Since findGetter and findSetter methods are not available to manipulate the name field in Cobra class, we have to manually build getters and setters in Cobra class. Let’s look at the following example:

public class Cobra {

  private String name;

  public String getName(a) {
    return name;
  }

  public void setName(String name) {
    this.name = name; }}public class RaceTest {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    Cobra cobra = new Cobra();
    MethodHandle getNameMethodHandle = lookup
      .findVirtual(Cobra.class, "getName", MethodType.methodType(String.class));
    MethodHandle setNameMethodHandle = lookup
      .findVirtual(Cobra.class, "setName", MethodType.methodType(void.class, String.class));
    setNameMethodHandle.invokeExact(cobra,"123"); System.out.println((String)getNameMethodHandle.invokeExact(cobra)); }}Copy the code

Although the above solution solves the problem of manipulating private fields of other classes, we built getter and setter methods. What else can we do without them? Can you do that by reflection? Just do it.

Scheme 2:

public class Cobra {

  private String name;
}

public class RaceTest {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    Cobra cobra = new Cobra();
    Field field = Cobra.class.getDeclaredField("name");
    field.setAccessible(true);
    MethodHandle methodHandle = lookup.unreflectSetter(field);
    methodHandle.invokeExact(cobra, "123"); methodHandle = lookup.unreflectGetter(field); System.out.println((String) methodHandle.invokeExact(cobra)); }}Copy the code

If you annotate the setAccessible method, you still get an error, but if you add this line of code, it’s related to the permissions of the reflection, and that’s not what we want.

Permission changes for JDK9

By accident, I found that the IMPLEMENTATION of the MethodHandles class in the JDK9 environment was different, including the handling of private attributes.

public class Cobra {

  private String name;
}


public class RaceTest {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    Cobra cobra = new Cobra();
    Lookup privateLookupIn = MethodHandles.privateLookupIn(Cobra.class, lookup);
    MethodHandle methodHandle = privateLookupIn.findSetter(Cobra.class, "name", String.class);
    methodHandle.invoke(cobra,"124");
    MethodHandle getterMethodHandle = privateLookupIn.findGetter(Cobra.class, "name", String.class); System.out.println(getterMethodHandle.invoke(cobra)); }}Copy the code

JDK9 introduces a new method privateLookupIn(Class
targetClass, MethodHandles.Lookup Lookup), which has the full functionality of Lookup object emulating all supported bytecode behavior (including private access) on the targetClass.

To summarize:

1. Access to the method handle does not depend on where the method handle was created, but on where the Lookup object was created.

2, if the JDK version is larger than 8, then you can also use this Lookup object to assign values to class private properties.

It is important to note that JDK9 has more permissions, and there are some pitfalls. Because method handles do not have runtime permission checking, the application is responsible for the management of method handles. Once it publishes some method handles that point to private methods, those private methods are exposed. As shown in the following cases:

public class Cobra {

  private void money(a){
    System.out.println("money"); }}public class RaceTest {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    Cobra cobra = new Cobra();
    Lookup privateLookupIn = MethodHandles.privateLookupIn(Cobra.class, lookup);

    MethodHandle methodHandle = privateLookupIn
      .findSpecial(Cobra.class, "money", MethodType.methodType(void.class), Cobra.class); methodHandle.invoke(cobra); }}Copy the code

Operation of a method handle

The invocation of method handles can be divided into two types. One is invokeExact, which requires strict matching of parameter types. How strict is it? Suppose a method handle receives an Object parameter. If you pass String as the actual parameter, the call to the method handle will throw a method type mismatch exception at run time. The correct call is to explicitly convert the String to Object.

The invokeExact is in the MethodHandle file

public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;
Copy the code

The method handle API has a special annotation class @polymorphicSignature. When it encounters a method call annotated by it, the Java compiler generates a method descriptor based on the declared type of the passed argument, rather than the one declared by the target method.

The following is an example:

public class Cobra {

  public void read(Object object){}}public class RaceTest {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    Cobra cobra = new Cobra();
    MethodHandle methodHandle = lookup
        .findVirtual(Cobra.class, "read", MethodType.methodType(void.class, Object.class));
    methodHandle.invokeExact(cobra,"123");
    methodHandle.invokeExact(cobra,(Object) "456"); }}// Compile RaceTest to view its bytecode
        13: ldc           #3                  // class com/msdn/java/hotspot/invokedynamic/Cobra
        15: ldc           #5                  // String read
        17: getstatic     #6                  // Field java/lang/Void.TYPE:Ljava/lang/Class;
        20: ldc           #7                  // class java/lang/Object
        22: invokestatic  #8                  // Method java/lang/invoke/MethodType.methodType:(Ljava/lang/Class; Ljava/lang/Class;) Ljava/lang/invoke/MethodType;
        25: invokevirtual #9                  // Method java/lang/invoke/MethodHandles$Lookup.findVirtual:(Ljava/lang/Class; Ljava/lang/String; Ljava/lang/invoke/MethodType;) Ljava/lang/invoke/MethodHandle;
        28: astore_3
        29: aload_3
        30: ldc           #10                 // String 123
        32: invokevirtual #11                 // Method java/lang/invoke/MethodHandle.invokeExact:(Ljava/lang/String;) V
        35: aload_3
        36: ldc           #12                 // String 456
        38: invokevirtual #13                 // Method java/lang/invoke/MethodHandle.invokeExact:(Ljava/lang/Object;) V
        41: return
Copy the code

Of course, the above code execution error, the following error:

Exception in thread "main" java.lang.invoke.WrongMethodTypeException: expected (Cobra,Object)void but found (String)void
Copy the code

If you need to automatically adapt parameter types, you can select invoke, the second call to the method handle. It is also a method of signature polymorphism. The invoke calls the methodhandle. asType method, generates an adapter MethodHandle, ADAPTS the passed parameter, and calls the original MethodHandle. The return value from the call to the original method handle is also adapted before being returned to the caller.

MethodHandle methodHandle = lookup
.findVirtual(Cobra.class, "read", MethodType.methodType(void.class, Object.class));
methodHandle.invoke(cobra,"123");
Copy the code

MethodHandle.asType

Method handles also support adding, deleting, or modifying parameters by generating another method handle. One of these changes is the methodHandle.astype method I just introduced.

MethodHandle methodHandle = lookup
  .findVirtual(Cobra.class, "read", MethodType.methodType(void.class, Object.class));
MethodHandle methodHandle1 = methodHandle
  .asType(methodHandle.type().changeParameterType(1,String.class));
methodHandle1.invokeExact(cobra,"123");
Copy the code

MethodHandles.dropArguments

DropArguments can add useless parameters to the parameters of a method handle. These parameters are not used in the actual call, but they can make the parameter type format of the transformed method handle conform to some of the desired specific patterns.

    MethodHandle methodHandle = lookup
        .findVirtual(Cobra.class, "read", MethodType.methodType(void.class, Object.class));
    MethodHandle methodHandle2 = MethodHandles.dropArguments(methodHandle, 1, String.class);
    methodHandle2.invokeExact(cobra,"123",(Object)"456");
Copy the code

The read method has only one Object parameter. We’ve added a String parameter with dropArguments. How do I see the new list of parameters for this method?

methodHandle2.invokeExact(cobra);
Copy the code

An error message is displayed when the preceding statement is executed:

Exception in thread "main" java.lang.invoke.WrongMethodTypeException: expected (Cobra,String,Object)void but found (Cobra)void
Copy the code

MethodHandles.insertArguments

This method can pre-bind specific values for multiple parameters in the method handle at the same time. In the resulting new method handle, parameters that have been bound to specific values no longer need to be supplied and do not appear in the parameter list.

public void read(Object object) {
  System.out.println("read" + object);
}

MethodHandle methodHandle = lookup
  .findVirtual(Cobra.class, "read", MethodType.methodType(void.class, Object.class));
MethodHandle methodHandle1 = MethodHandles.insertArguments(methodHandle, 1."999");
methodHandle1.invokeExact(cobra);// The output is read999
Copy the code

MethodHandle.bindTo

The bindTo method of MethodHandle allows you to pre-bind the call receiver of the underlying method, passing in the actual parameters instead of specifying the method receiver when the actual call is made.

public voidBindTo ()throwsThrowable{MethodHandles.Lookup Lookup =MethodHandles. Lookup ();/ / MethodHandle bindTo use
  MethodHandle methodHandle = lookup
    .findVirtual(String.class, "length", MethodType.methodType(int.class));
  int len = (int) methodHandle.invoke("java");
  System.out.println(len);
  methodHandle = methodHandle.bindTo("hello java");
  len = (int) methodHandle.invoke();
  System.out.println(len);
}
Copy the code

It can be seen that

  • The first is called without binding and requires passing in the receiver of the Length method.
  • The second method prebinds an object of the String class, so it does not need to be specified when called.

There is much more to be said about method handles than is covered here, but Java method handles —–1 is recommended. Method handle type, call

Implementation of method handles

As mentioned earlier, the invokeExact or Invoke methods used to invoke method handles have signature polymorphisms. They generate method descriptors based on specific incoming parameters. So, does a way to have this descriptor actually exist? Which method does a call to invokeExact or Invoke enter?

//JDK9 public class CallPathTest { public static void bar(Object o) { new Exception().printStackTrace(); } public static void main(String[] args) throws Throwable { MethodHandles.Lookup l = MethodHandles.lookup(); MethodType t = MethodType .methodType(void.class, Object.class); MethodHandle mh = l.findStatic(CallPathTest.class, "bar", t); mh.invokeExact(new Object()); }}Copy the code

We can see the stack trace by creating a new exception instance in the same way we can see the reflected call. The printed occupation trajectory is as follows:

java.lang.Exception
	at com.msdn.java.hotspot.invokedynamic.CallPathTest.bar(CallPathTest.java:15)
	at com.msdn.java.hotspot.invokedynamic.CallPathTest.main(CallPathTest.java:23)
Copy the code

That is, the target method of invokeExact is the method to which the method handle points.

We need to verify the type of method handle before executing the target method, and we can set JVM parameters to output more information. Parameters as follows:

-XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames
Copy the code

The output of JDK8 and JDK9 is different.

//JDK9
java.lang.Exception
	at com.msdn.java.hotspot.invokedynamic.CallPathTest.bar(CallPathTest.java:15)
	at java.base/java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(DirectMethodHandle$Holder:1000010)
	at java.base/java.lang.invoke.LambdaForm$MH000/1146848448.invokeExact_000_MT(LambdaForm$MH000:1000019)
	at com.msdn.java.hotspot.invokedynamic.CallPathTest.main(CallPathTest.java:23)
	
//JDK8
java.lang.Exception
	at com.msdn.java.hotspot.invokedynamic.CallPathTest.bar(CallPathTest.java:15)
	at java.lang.invoke.LambdaForm$DMH001/1950409828.invokeStatic_001_L_V(LambdaForm$DMH001:1000010)
	at java.lang.invoke.LambdaForm$MH012/1067040082.invokeExact_000_MT(LambdaForm$MH012:1000016)
	at com.msdn.java.hotspot.invokedynamic.CallPathTest.main(CallPathTest.java:26)
Copy the code

The final execution link is different depending on the environment. Below we will demonstrate the code in the JDK9 environment and finish with an additional description of the JDK8 call link.

Next we need to find a way to get the corresponding class file, first the class file associated with the LambdaForm (hereafter we will call it the adapter), Can be derived by adding virtual machine parameters into the class files (- Djava. Lang. Invoke. MethodHandle. DUMP_CLASS_FILES = true).

The resulting class file looks like this:

final class LambdaForm$MH000 {
  @Hidden
  @Compiled
  @ForceInline
  static void invokeExact_000_MT(Object var0, Object var1, Object var2) {
    MethodHandle var3;
    Invokers.checkExactType(var3 = (MethodHandle)var0, (MethodType)var2);
    Invokers.checkCustomized(var3);
    var3.invokeBasic(var1);
  }

  static void dummy(a) {
    String var10000 = "MH.invokeExact_000_MT=Lambda(a0:L,a1:L,a2:L)=>{\n t3:V=Invokers.checkExactType(a0:L,a2:L); \n t4:V=Invokers.checkCustomized(a0:L); \n t5:V=MethodHandle.invokeBasic(a0:L,a1:L); void}"; }}Copy the code

As you can see, in this adapter, it calls the Invokers.checkExactType method to check the parameter types, and then calls the Invokers.checkCustomized method. In-depth look at its source, the number of executions in method handles exceeds a threshold optimization (corresponding parameters – Djava. Lang. Invoke. MethodHandle. CUSTOMIZE_THRESHOLD, the default value is 127).

    static final int CUSTOMIZE_THRESHOLD;
    CUSTOMIZE_THRESHOLD = Integer.parseInt(
                props.getProperty("java.lang.invoke.MethodHandle.CUSTOMIZE_THRESHOLD"."127"));
    
    
    @DontInline
    void maybeCustomize(MethodHandle mh) {
        byte count = mh.customizationCount;
        if (count >= CUSTOMIZE_THRESHOLD) {
            mh.customize();
        } else {
            mh.customizationCount = (byte)(count+1); }}Copy the code

Finally, it invokes the invokeBasic method on the method handle.

If you loop through invokeExact more than 127 times, you will see that the invocation link changes.

The invokeBasic method called last is also a local method, and we try to find the DirectMethodHandle$Holder file using HSDB.

In the reflection section, we also encountered the inflationThreshold. If the threshold is exceeded, a Java bytecode file will be generated, which is more efficient than the Native version of reflection calls. So what about the CUSTOMIZE_THRESHOLD optimization? Next through debugging to feel the subtlety of this fast program.

1. By default, CUSTOMIZE_THRESHOLD=127, and the checkCustomized method sees that mh.form — the entry to the adapter is DirectMethodHandle$Holder.

2. Set CUSTOMIZE_THRESHOLD to 0. When the checkCustomized method is executed, mh.form is the same as the figure in the preceding figure, but because count >= CUSTOMIZE_THRESHOLD is true. The customize method is then executed.

    void customize(a) {
        if (form.customized == null) {
            LambdaForm newForm = form.customize(this);
            updateForm(newForm);
        } else {
            assert(form.customized == this); }}Copy the code

As you can see, a new LambdaForm object is created in this method, and then the updateForm method is executed to replace the new LambdaForm object into memory.

    void updateForm(LambdaForm newForm) {
        assert(newForm.customized == null || newForm.customized == this);
        if (form == newForm)  return;
        newForm.prepare();  // as in MethodHandle.<init>
        UNSAFE.putObject(this, FORM_OFFSET, newForm);
        UNSAFE.fullFence();
    }
Copy the code

The next time you run the invokeExact method, the checkCustomized method is displayed because mh.form.customized is not null, and you exit directly.

The LambdaForm adapter is different, so where does optimization come in? Based on our test code above, Look at the default DirectMethodHandle holder.class and lambdaFormHolder.class generated after the threshold has been reached Compare lambdaformHolder.class with the lambdaformDMh000.class generated when the threshold is reached (which can be generated using the DUMP_CLASS_FILES parameter).

//DirectMethodHandle$Holder
  @Hidden
  @Compiled
  @ForceInline
  static Object invokeStatic(Object var0, Object var1) {
    Object var2 = DirectMethodHandle.internalMemberName(var0);
    return MethodHandle.linkToStatic(var1, (MemberName)var2);
  }
  
//LambdaForm$DMH000
  @Hidden
  @Compiled
  @ForceInline
  static void invokeStatic_001_L_V(Object var0, Object var1) {
    MethodHandle var3 = (MethodHandle)"CONSTANT_PLACEHOLDER_1 <<com.msdn.java.hotspot.invokedynamic.CallPathTest.bar(Object)void/invokeStatic>>";
    Object var2 = DirectMethodHandle.internalMemberName(var3);
    MethodHandle.linkToStatic(var1, (MemberName)var2);
  }
Copy the code

As you can see, DirectMethodHandle calls the internalMemberName method to get a field of type MemberName and continues with the linkToStatic call.

The difference: In the invokeStatic method of the LambdaForm$DMH000 file, DirectMethodHandle treats the method handle as a constant, resulting in a field of type MemberName.

To sum up, when a MethodHandle MethodHandle is called multiple times, the invokers.checkcustomized method generates a specific adapter for that MethodHandle. This particular adapter takes the method handle as a constant, directly fetches its MemberName type field, and continues the subsequent linkToStatic call.

extension

With these optimizations out of the way, let’s go back to the JDK8 call link.

// JDK8, JVM parameters are: -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true java.lang.Exception at com.msdn.java.hotspot.invokedynamic.CallPathTest.bar(CallPathTest.java:15) at java.lang.invoke.LambdaForm$DMH001/1950409828.invokeStatic_001_L_V(LambdaForm$DMH001:1000010) at java.lang.invoke.LambdaForm$MH012/1067040082.invokeExact_000_MT(LambdaForm$MH012:1000016) at com.msdn.java.hotspot.invokedynamic.CallPathTest.main(CallPathTest.java:26)Copy the code

We ended up with a bunch of class files with the LambdaForm prefix.

Based on the above analysis, the shared adapter should be used before optimization, but why not here?

Let’s first look at the contents of the class file used:

final class LambdaForm$DMH001 {
  @Hidden
  @Compiled
  @ForceInline
  static void invokeStatic_001_L_V(Object var0, Object var1) {
    Object var2 = DirectMethodHandle.internalMemberName(var0);
    MethodHandle.linkToStatic(var1, (MemberName)var2);
  }

  static void dummy(a) {
    String var10000 = "DMH.invokeStatic_001_L_V=Lambda(a0:L,a1:L)=>{\n t2:L=DirectMethodHandle.internalMemberName(a0:L); \n t3:V=MethodHandle.linkToStatic(a1:L,t2:L); void}"; }}final class LambdaForm$MH012 {
  @Hidden
  @Compiled
  @ForceInline
  static void invokeExact_000_MT(Object var0, Object var1, Object var2) {
    Invokers.checkExactType(var0, var2);
    Invokers.checkCustomized(var0);
    MethodHandle var3;
    (var3 = (MethodHandle)var0).invokeBasic(var1);
  }

  static void dummy(a) {
    String var10000 = "invokeExact_000_MT=Lambda(a0:L,a1:L,a2:L)=>{\n t3:V=Invokers.checkExactType(a0:L,a2:L); \n t4:V=Invokers.checkCustomized(a0:L); \n t5:V=MethodHandle.invokeBasic(a0:L,a1:L); void}"; }}Copy the code

The adapter used before optimization was not DirectMethodHandle$Holder, but the invokeStatic_001_L_V method was nothing special. We tried to change the value of CUSTOMIZE_THRESHOLD to 0. Then take a look at the optimized generated adapter.

View the content of the file as follows:

final class LambdaForm$DMH009 {
  @Hidden
  @Compiled
  @ForceInline
  static void invokeStatic_007_L_V(Object var0, Object var1) {
    MethodHandle var3 = (MethodHandle)"CONSTANT_PLACEHOLDER_0 <<com.msdn.java.hotspot.invokedynamic.CallPathTest.bar(Object)void/invokeStatic>>";
    Object var2 = DirectMethodHandle.internalMemberName(var3);
    MethodHandle.linkToStatic(var1, (MemberName)var2);
  }

  static void dummy(a) {
    String var10000 = "DMH.invokeStatic_007_L_V=Lambda(a0:L,a1:L)=>{\n t2:L=DirectMethodHandle.internalMemberName(a0:L); \n t3:V=MethodHandle.linkToStatic(a1:L,t2:L); void}"; }}Copy the code

The content, like the optimized content in JDK9, will make method handles constant. It seems that JDK8 and JDK9 end up executing the same, but it is not clear why the call links are different.

reference

Reflection of JAVA basics

Java method handle —–2. Method handle acquisition, transformation, special method handle

Geek time “In-depth dismantling of Java virtual machine” Zheng Yudi