in JDK7, Java provides support for dynamic language features, enabling the JSR 292 Supporting dynamically Typed Languages on the Java Platform specification, this is a major advance in the development of the Java language, while providing support for dynamic language features is also a major trend and direction of Java development. So where is the dynamic? At the Java API level, a new Java.lang.invoke package is added, including Callsite, Methodhandle, Methodtype, and so on, and second, at the Java bytecode directive level, a new invokedynamic command is added, With the addition of the Invokedynamic directive added Constant_invokedynamic_info, Constant_methodhandle_info, in the class file constant pool, Constant_ METHODTYPE_INFO constant table, new bootstrapmethods attribute table.
So what is dynamic, relative to the dynamic is static. Everyone should have heard that Java is a static language (c + +), while the dynamic language has groovy, JavaScript, Ruby, Phthon, PHP, Lisp and so on. From this enumeration can be found that dynamic language a large pile, and static language is the most common Java and C + +, which also from the side of the evidence of dynamic is the trend of language development. The most important feature of these dynamic languages is that variables are declared with Var/def, but in Java, the type of the variable must be specified when declaring a variable, such as String name = "Zhangsan". In a deep sense, the variable of a dynamic language declaration cannot determine the specific type of the variable at compile time, only the concrete type of the variable can be determined at run time, whereas the static language, the concrete type of the variable (in this case, the static type of the variable, which is not included here) has been determined at compile time. In Java, for example: declares an instance variable, after the compiler compiles, the variable's simple name and descriptor symbol reference are stored in the class file bytecode, during the parsing phase of the classes, the virtual machine will resolve the symbolic reference of the variable to a direct reference. This direct reference will parse the reason for the type, which has been determined at compile time. Here's a specific example:
Dynamic language with JavaScript as an example
function output () {}output.prototype.println = function (_value) {console.info (_value);//firefox in}//execute statement var output = New Output (); Output.println ();
In the above code, the output type object has a println method, and in the actual run, the variable output does not necessarily have to be an output type. As long as the output variable points to the object (the recipient of the method) there is a println method on it, regardless of what type the receiver is.
In Java, take the most famous HelloWorld as an example:
public static void Main (string[] args) {System.out.println ("HelloWorld");}
System.out is declared as a java.io.PrintStream type, so the System.out object must be a Java.io.PrintStream type or a subclass of Java.io.PrintStream. Here you may conversation, this is not true type is also can change. True, but this "change" has a great limit, the object's true type must be a type of declaration or a subtype of the declared type, which is the most basic guarantee of linguistic polymorphism. And JDK7 's support for dynamic is to do the same thing in type JavaScript, where the real type of the object is determined by the run time.
Here is an example of using the Java.lang.invoke package to complete a dynamic method invocation:
import Java.lang.invoke.methodhandle;import Java.lang.invoke.methodhandles;import Java.lang.invoke.methodtype;import Java.util.random;public class Output {public void println (Object value) {System.out.println ("value=" + value);} public static Methodhandle Getmethodhandle (object receiver) throws Throwable {//If the Lookup object Methodhandles.lookup lookup = Methodhandles.lookup ();//methodtype represents the type of the method (does not contain the method name), in fact Methodtype is to determine the descriptor of the method, for example, this method descriptor is: (Ljava/lang/object;) Vmethodtype Methodtype = Methodtype.methodtype (Void.class, object.class);//Find a virtual method named println in the receiver class that specifies the method type return Lookup.findvirtual (Receiver.getclass (), "println", Methodtype). BindTo (receiver); public static void Main (string[] args) throws Throwable {Object receiver = new Random (). Nextint (1000)%2==0? System.out:new Output ();//regardless of receiver's eventual type, the method can be called normally as long as there is a println method. Getmethodhandle (receiver). Invoke ("Hello Dynamic Invoke");}}
you will find that at this time, no matter what type of receiver is the object, as long as it has a method named println with a parameter, the program can run, so that the method receiver can be dynamically determined.
From the above example, Java dynamic use is very simple, but after reading its usage, you may have doubts, the same thing, with reflection is not long-time to achieve it? Indeed, only in the Java language perspective, the use of methodhandle and effects and reflection have many similarities. However, they also have the following differences:
1. The reflection and methodhandle mechanisms are essentially simulated method invocations, but reflection is a method call that emulates the Java code hierarchy, and Methodhandle is a method call that emulates the byte-code hierarchy. The three methods on the Methodhandles.lookup findstatic (), findvirtual (), findspecial () are designed to correspond to Invokestatic, Invokevirtual & The Invokeinterface and invokespecial execute permission check behavior for these bytecode directives, and these underlying details do not need to be concerned when using the reflection API.
2. The Java.lang.reflect.Method object in reflection is far more than the information contained in the Java.lang.invoke.MethodHandle object in the Methodhandle mechanism. The former is a comprehensive image of the method at the Java end, including the method's signature, descriptor, and the Java side representation of the various attributes in the method attribute table, as well as run-time information such as execution permissions. The latter only contains information related to the execution of the method. In the popular words of developers, reflection is heavyweight, while Methodhandle is lightweight.
3. Since Methodhandle is a simulation of the method instruction invocation of bytecode, the various optimizations that the virtual machine makes in this regard, such as inline methods, should also be supported in a similar way on methodhandle (but the implementation is not yet complete). It is not possible to invoke the method by reflection.
Methodhandle and Reflection In addition to the above-mentioned differences, the most critical point is to remove the premise of the previous discussion imposed "only stand in the Java language perspective" after: Reflection API is designed to serve only the Java language, The Methodhandle is designed to serve all Java virtual machines, including the Java language.
Before JDK7, the bytecode directive used for method invocation was only Invokeinterface, Invokestatic, Invokespecial, invokevitual four, the specific meaning and behavior of the four instructions can refer to the specific information, The most authoritative of course is the Java Virtual Machine specification. These four instruction method invocations are summarized with 4 elements:
1. Method Name: The name of the method to invoke is typically the symbol name specified by the developer in the source code. This name will also appear in the compiled byte code.
2. Link: The link contains the class to invoke the method. This step may involve loading the class.
3. Select: Select the method you want to invoke. Select the method to invoke in the class based on the method name and parameters.
4. Adaptation: The caller and receiver agree on the way the call is made, that is, to agree on the type declaration of the method.
Once the above 4 elements have been identified, the Java Virtual Opportunity Transfers control to the called method and passes the actual parameters of the call to the past.
These 4 instructions have a short board in terms of dynamic, such as the method name and descriptor in the bytecode has been determined to be unable to change, 4 instructions with a running constant pool index parameters, point to a constant_methodref_info table, Constant_ The Methodref_info table contains information about the class in which the method resides (Constant_class_info index), so that the class of the method cannot be dynamically changed, and it determines that the receiver of the method cannot be dynamically altered (polymorphism is not included here).
In JDK7, the invokedynamic directive is added, and the instruction format is as follows:
Indexbyte1 and Indexbyte2 make up a run-time constant pool index, which is an constant_invokedynamic table, also called the call site specifier, and the 3rd and 4th operands must be 0. The Constant_invokedynamic table contains information such as the Startup method (bootstrap methods), the dynamic Join method name return value parameter list, and so on. One thing to note is that the Constant_invokedynamic table Bootstrap_method_attr_index data item is not an index of the run-time pool, but rather a starting method array index contained in the Bootstrapmethods property. See the detailed description of the invokedynamic directive in the Java Virtual Machine specification, as well as this article.
The invokedynamic directive relaxes the limits of method invocations and increases the flexibility of method calls, with the four elements called by the above method to illustrate:
1. In terms of the name of the method, it is not necessarily a string that conforms to the Java naming convention and can be arbitrarily specified. The caller and provider of the method do not need to agree on the method name.
2. Provides a more flexible way to link. The method that is actually called by a method call can be determined at run time. This is equivalent to deferring the link operation to the runtime, rather than having to be determined at compile time. For an already linked method call, you can also re-link it and point it to another method.
3. In terms of method selection, it is no longer possible to send the pie only on the receiver of the method invocation, but rather to consider all parameters at the time of invocation, that is, to support the multi-Dispatch of the method.
4. Before calling, parameters can be processed in various ways, including type conversion, adding and removing parameters, collecting and distributing variable-length parameters, and so on.
If the example of the above dynamic method invocation executes the JAVAP command, the following results are obtained (Getmethodhandle and Main method):
public static Java.lang.invoke.MethodHandle Getmethodhandle (Java.lang.Object) throws java.lang.Throwable; Flags:acc_public, acc_static exceptions:throws java.lang.Throwable code:stack=4, locals=3, args_size=1 0:invokestatic #48//Method Java/lang/invoke/methodhandles.lookup: () Ljava/lang/invoke/methodhan Dles$lookup; 3:astore_1 4:getstatic #54//Field Java/lang/void.type:ljava/lang/class; 7:LDC #3//class Java/lang/object 9:invokestatic #60//Method Java /lang/invoke/methodtype.methodtype: (Ljava/lang/class; Ljava/lang/class;) Ljava/lang/invoke/methodtype; 12:astore_2 13:aload_1 14:aload_0 15:invokevirtual #66//Met Hod Java/lang/object.getclass: () Ljava/lang/class; 18:LDC #70//String println 20:aload_2 21:invokeVirtual #71//Method java/lang/invoke/methodhandles$lookup.findvirtual: (Ljava/lang/class; ljava/lang/string; Ljava/lang/invoke/methodtype;) Ljava/lang/invoke/methodhandle; 24:aload_0 25:invokevirtual #77//Method java/lang/invoke/methodhandle.bindto: (Ljava/lang /object;) Ljava/lang/invoke/methodhandle; 28:areturn linenumbertable:line 16:0 line 18:4 line 20:13 localvariabletable: Start Length Slot Name Signature 0 0 receiver Ljava/lang/object; 4 1 Lookup Ljava/lang/invoke/methodhandles$lookup; 2 Methodtype Ljava/lang/invoke/methodtype; public static void Main (java.lang.string[]) throws java.lang.Throwable; Flags:acc_public, acc_static exceptions:throws java.lang.Throwable code:stack=2, locals=2, args_size=1 0:new #87//Class Java/utIl/random 3:dup 4:invokespecial #89//Method java/util/random. " <init>:() V 7:sipush 10:invokevirtual #90//Method Java/util/random.ne Xtint: (i) I 13:iconst_2 14:irem 15:ifne 18:getstatic #16 Field Java/lang/system.out:ljava/io/printstream; 21:goto 24:new #1//Class Com/xtayfjpk/asm/test/dynamicinvoke/demo/output 27:dup 28:invokespecial #94//Method "<init>":() V 31:astore_1 32:aload_1 33:invokestatic #95//Method Getmethodhandle: (ljava/lang/object;) Ljava/lang/invoke/methodhandle; 36:LDC #97//String Hello Dynamic Invoke 38:invokevirtual #99//Metho D Java/lang/invoke/methodhandle.invoke: (ljava/lang/string;) V 41:return linenumbertable:line 26:0 line 28:32 line 29:41 Localvariabletable:start Length Slot Name Signature 0 0 args [ljava/lang/string ; 1 receiver Ljava/lang/object; Stackmaptable:number_of_entries = 2 Frame_type = +/same */Frame_type = +//same_locals_1_stack _item */stack = [Class Java/lang/object]
You will find that in the code attribute, the shadow of the invokedynamic directive is not completely found, because the invokedynamic instruction is provided to the dynamic compiler, and we compile with the Javac compiler. Javac it does not generate invokedynamic directives. As programmers, more still use the class in the Java.lang.invoke package to the dynamic invocation of the full method, if you implement some kind of dynamic compiler in the code attribute generated invokedynamic instructions, the virtual machine can still be executed normally.
Each invokedynamic instruction that appears in the byte code becomes a dynamic call site. Each dynamic call point is in an unlinked state when it is initialized. At this time, this dynamic call point is not specified to invoke the actual method. When the virtual machines execute the dynamic instruction, they first link to the call point, and the dynamic call point is determined by a method called the Startup method (bootstrap), and the return value of the Startup method is Callsite. A methodhandle, called a callsite target, is bound on the Callsite, and the Methodhandle method handle can be used to navigate to the method that is actually executing. In other words, the call to the invokedynamic instruction is actually equivalent to a call to the method handle, specifically a call to the Invoke method that is converted to the method handle.
In JDk7, there are three kinds of callsite, respectively Constantcallsite,mutablecallsite and Volatilecallsite, and these three classes are callsite subclasses. Constantcallsite is characterized by its goal binding is permanent, but the binding can no longer be changed, that is, a invokedynamic instruction link on a constantcallsite, its Methodhandle method handle can no longer change. Mutablecallsite and Constantcallsite are relative and can be changed after the target is bound. The volatilecallsite target has the characteristics of a volatile variable, and when the invokedynamic is linked to a volatilecallsite call point, the change to the target of the call point is immediately observable by the invokedynamic instruction. Even if this change is done in a different thread.
Below we will manually generate the invokedynamic instruction to see if the virtual machine can perform properly:
Because the bytecode generated by the Javac compilation does not contain the INVOKEDYNAMIC directive, we cannot see it. However, we can use bytecode generation tools (such as ASM) to generate them manually.
Import static Org.objectweb.asm.opcodes.*;import Java.lang.invoke.callsite;import Java.lang.invoke.constantcallsite;import Java.lang.invoke.methodhandle;import Java.lang.invoke.MethodType;import Java.lang.invoke.methodhandles.lookup;import Java.nio.file.files;import Java.nio.file.paths;import Org.objectweb.asm.classwriter;import Org.objectweb.asm.handle;import Org.objectweb.asm.methodvisitor;public Class Dynamicinvokeinstructiongenerator {//Startup method defines public static CallSite bootstrap (lookup lookup, String name, Methodtype type , String value) throws Exception {Methodhandle handle = lookup.findvirtual (Stringbuilder.class, name, Methodtype.methodtype (Stringbuilder.class)). BindTo (New StringBuilder (value)); return new Constantcallsite (handle);} The method handle defined in ASM is private static final Handle BSM = new Handle (h_invokestatic, DynamicInvokeInstructionGenerator.class.getName (). replace ('. ', '/'), ' Bootstrap ', Methodtype.methodtype ( Callsite.class, Lookup.class, String.class, Methodtype.class, String.class). ToMEthoddescriptorstring ());p ublic static void Main (string[] args) throws Exception {Classwriter cw = new Classwriter (CLASSW Riter. Compute_frames); Cw.visit (V1_7, acc_public| Acc_super, "Stringreverser", NULL, "Java/lang/object", null);//Generate Main method Methodvisitor mv = Cw.visitmethod (acc_public| Acc_static, "main", "([ljava/lang/string;) V", NULL, NULL); Mv.visitcode (); MV.VISITFIELDINSN (getstatic, "java/lang/ System "," Out "," ljava/io/printstream; "); /Call StringBuilder's Reverse method mv.visitinvokedynamicinsn ("Reverse", "() Ljava/lang/stringbuilder;", BSM, "Hello Dynamic Invoke ");//Generate invokedynamic instruction//Call SYSTEM.OUT.PRINTLN (Object x) mv.visitmethodinsn (invokevirtual," java/io/ PrintStream "," println "," (Ljava/lang/object;) V "); mv.visitinsn (RETURN); mv.visitmaxs (0, 0); Mv.visitend (); Cw.visitend (); Files.write (Paths.get ("Stringreverser.class"), Cw.tobytearray ());}}
Just for the convenience of writing, the startup method, define the method handle, the code that generated the bytecode is written in a class, and the generated Stringreverser class executes the JAVAP command to get if the result:
{public static void Main (java.lang.string[]); Flags:acc_public, acc_static Code: stack=2, Locals=1, args_size=1 0:getstatic #12 //Field Java /lang/system.out:ljava/io/printstream; 3:invokedynamic #25, 0 //invokedynamic #0: Reverse: () Ljava/lang/stringbuilder; 8:invokevirtual #31 //Method java/io/printstream.println: (ljava/lang/object;) V 11:return}
as you can see, the invokedynamic directive does generate, executing the class to get output: Ekovni cimanyd Olleh, correctly performing a string reverse sequence operation.
The bytecode is manually generated and executed sequentially.
For more detailed information about starting a method signature, Java.lang.invoke the class in the package, see: http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/package-summary.html
JDK7 Dynamic Method Invocation