Explain the invoke method of method in Java
When writing code, it is found that the method obtained from the parent class through getdeclaredmethod can call the object of the subclass, and the subclass rewrites this method. The method can also be obtained from the subclass class through getdeclaredmethod. At this time, the object calling the parent class will also report an error. Although this is very consistent with polymorphism and Java's dynamic binding specification, I still want to understand how Java is implemented, so I learned the source code of method. Invoke method of method
1. First check whether the override property of accessibleobject is true.
Accessibleobject is the parent class of method, field and constructor. The override property is false by default. You can call setaccessible method to change it. If it is set to true, it means that you can ignore the restriction of access permission and call it directly.
2. If it is not true, access rights should be detected. Use the quickcheckmemberaccess method of reflection to check whether it is public. If not, use reflection Getcallerclass (1) method gets
Get the class that calls this method, and then verify whether it has access permission. After verification, cache it once, so that if this class calls next time, you don't have to do verification, and directly use the last result, (it's strange to cache in this way, because if you change a class to call next time, you won't need to cache. Verify again and take the result this time as the cache, but the last cache result will be flushed out. This is a very simple caching mechanism, which is only applicable to repeated calls of one class).
3. Call the invoke method of methodaccessor. Each method object contains a root object, which holds a methodaccessor object. The method we obtained is exclusive, which is equivalent to the image of a root object. All such methods share the methodaccessor object in root (this object is generated by the reflectionfactory method, which is static final in the method class and instantiated by the native method).
Reflectionfactory generates a methodaccessor: if the noinflation property is true, a methodaccessor created by the methodaccessorgenerator will be returned directly. Otherwise, the delegatingmethodaccessorimpl is returned and referenced with a nativemethodaccessorimpl. However, when delegatingmethodaccessorimpl executes the invoke method, it is delegated to nativemethodaccessorimpl.
Go further
4. Invkoe method of nativemethodaccessorimpl:
Call the native method invoke0 to execute the method call
Note that there is a counter numinvocations, which is + 1 for each method call, when compared with reflectionfactory When the inflation threshold (15) is large, create a methodaccessor with the methodaccessorgenerator, and replace the previous delegatingmethodaccessorimpl reference with the newly created one. The next delegatingmethodaccessorimpl will not be executed by nativemethodaccessorimpl, but by the methodaccessor of the newly generated Java bytecode.
Methodaccessorgenerator uses ASM bytecode dynamic loading technology, which will not be studied in depth for the time being.
In summary, a method can generate multiple method objects, but there is only one root object, which is mainly used to hold a methodaccessor object. This object can also be considered that there is only one method, which is equivalent to static. Because the method invoke is executed by the methodaccessor, the answer I want to know is in the methodaccessor invoke. Go deep into the methodaccessor:
------------------------------------------Methodaccessor ---------------------------------------- if there is such a Class A:
You can write another class to reflect and call the method on a:
Note that there will be no symbolic dependency on Class A on the testclassload class -- that is, when loading and initializing the testclassload class, you do not need to care about the existence of class A, but wait until the main () method executes to call class Class A is dynamically loaded only when forname(); Here, a parameter version of forname () is used, that is, the classloader of the class where the current method is located is used to load and initialize the newly loaded class Well, this detail has nothing to do with the theme.
Back to the subject. My test environment this time is sun's JDK 1.6 0 update 13 build 03。 Compile the above code and add the - XX: + traceclassloading parameter (or - verbose: class or - verbose directly) when executing testclassload, as follows:
Console commands
java -XX:+TraTestClassLoad ceClassLoading
You can see that a lot of logs are output, and the relevant parts are intercepted as follows:
It can be seen that the first 15 reflection calls to the A.Foo () method are not unusual, but something seems to be triggered during the 16th reflection call, resulting in a new pile of classes loaded by the JVM, including [loaded sun. Reflect. Generated methodaccessor1 from _ JVM defineclass] Such a line. Where did this come from?
Let's take a look at the method in JDK How invoke () is implemented.
You can see method Invoke () is not actually the reflection calling logic implemented by itself, but is delegated to sun reflect. Methodaccessor.
Each actual Java method has only one corresponding method object as root,. This root will not be exposed to the user. Instead, each time a method object is obtained through reflection, a new method object is created to wrap the root and give it to the user. Before calling the invoke () method of the method object corresponding to an actual Java method for the first time, the methodaccessor object implementing the calling logic has not been created; When the first call is made, the MethodAccessor is newly created and updated to root, and then called MethodAccessor.. Invoke () actually completes the reflection call.
So what is a methodaccessor?
You can see that it is just a single method interface, and its invoke () method is the same as method Corresponding to invoke().
The methodaccessor instance is created by reflectionfactory.
Here you can see interesting places. As mentioned in the note, the actual implementation of methodaccessor has two versions, one is implemented in Java and the other is implemented in native code. The version implemented in Java takes more time to initialize, but the performance is good in the long run; The native version is just the opposite. It starts relatively fast, but after a long run time, it is no faster than the Java version. This is the performance characteristic brought by the optimization method of hotspot, and it is also the common point of many virtual machines: crossing the native boundary will hinder the optimization. It is like a black box, making it difficult for the virtual machine to analyze and inline, so the managed version of the code will be faster after a long running time.
In order to balance the performance of the two versions, sun's JDK uses the "inflation" technique: let Java methods use the native version for the first few times when they are called by reflection. When the number of reflection calls exceeds the threshold, a special methodaccessor implementation class will be generated to generate the bytecode of the invoke () method, and the Java version will be used for reflection calls to the Java method in the future.
Sun's JDK has adopted this optimization since the 1.4 series.
PS. you can add - dsun. To the startup command reflect. If noinflation = true, the noinflation property of refactionfactory will become true. In this way, the program will use the Java version of methodaccessor at the beginning without waiting for 15 calls.
You can see reflectionfactory Newmethodaccessor() produces the logic of methodaccessor. The delegatingmethodaccessorimpl code used in "first several times" is as follows:
This is an indirect layer to facilitate switching between native and Java version of methodaccessor.
Then, the following is the statement on the Java side of the native version of methodaccessor:
Every time nativemethodaccessorimpl When the invoke () method is called, a call count will be added to see if it exceeds the threshold; Once exceeded, methodaccessorgenerator. Is called Generatemethod() to generate the implementation class of the Java version of methodaccessor, and change the methodaccessor referenced by delegatingmethodaccessorimpl to the Java version. Follow up via delegatingmethodaccessorimpl Invoke () calls the Java implementation.
Notice that the key invoke0 () method is a native method. It is managed by the JVM in the hotspot VM_ Supported by invokemethod() function:
Written by C
JVM_ END
The key is reflection:: invoke_ method():
If you go further, you will go deep into the interior of hotspot VM. This article will stop here. If you are interested in studying deeply, you can write another article to discuss the implementation of native version in the future.
What does the methodaccessorgenerator look like? Because the code is too long, it is not completely posted here. Its basic work is to generate new special Java classes in memory and load them. Just paste this method:
If you read the source code, you can see how the methodaccessorgenerator produces the Java version of the methodaccessor implementation class bit by bit. You can also see where the name generatedmethodaccessor + number comes from, which is in the generatename () method above. The Java version of methodaccessor generated by A. foo () of the example at the beginning of this article is roughly as follows:
In terms of reflection calls, the invoke () method is very clean (however, the overhead is obvious in terms of "normal calls"). Notice that the parameter array is disassembled, restore each parameter to the original state before it was wrapped by object [], and then make a normal invokevirtual call to the target method. Because the array of parameter types has been iterated during code generation, the generated code will no longer contain loops.
So far, I have found my answer, because methodaccessor will force type conversion and then make method calls, but an error will be reported when the parent class is forcibly converted to a child class. The type mismatch error will be reported. Therefore, if the reference declaration of the variable is the parent but the object actually pointed to is the child, this call is also possible.
When the reflection call becomes a hotspot, it can even be inlined close to method The side of invoke () greatly reduces the overhead of reflection calls. The reflection call of native version cannot be effectively inlined, so the call overhead cannot be reduced with the running of the program.
Although the implementation of sun's JDK makes the cost of reflection calling method much lower than before, method Invoke () itself should wrap the parameters in an array; Moreover, each call must check the visibility of the method (in method. Invoke()), and the type matching of each actual parameter and formal parameter (in nativemethodaccessorimpl. Invoke0() or the generated java version of methodaccessor. Invoke()); And method Invoke () is like a single wooden bridge. Reflection calls everywhere have to be crowded. The type information collected at the call point will be very messy, affecting the judgment of the inline program, making method Invoke () itself is difficult to inline to the caller.
In contrast, the new methodhandler in JDK7 has more potential. After its function is fully implemented, it can achieve higher performance than ordinary reflection calling methods. When using methodhandle for reflection calls, methodhandle The formal parameters and return value types of invoke () are accurate, so you only need to check the type matching when linking methods, rather than checking every call. Moreover, the methodhandle is an immutable value, and its internal state will not change after creation; The JVM can use this knowledge to make radical optimization, such as inlining the actual call target to the side where the reflection call is made.
Originally, Java's security mechanism makes it impossible to see any information among different classes, but there is an opening in sun's JDK, and a tag class is specially used to open the back door:
package sun. reflect;
That "_jvm_defineclass_" The source of is here:
P. The "shared objects file" in S. log is actually rt.jar. Why is it displayed like this? There is an answer on stack overflow:
This is Class Data Sharing. When running the Sun/Oracle Client HotSpot and sharing enable (either -Xshare:auto which is the default,or -Xshare:on),the classes. jsa file is memory mapped. This file contains a number of classes (listed in the classlist file) in internal representation suitable for the exact configuration of the machine running it. The idea is that the classes can be loaded quickly,getting the the JVM up faster. Soon enough a class not covered will be hit,and rt.jar will need to be opened and classes loaded conventionally as required.
I can't understand it very well. It is generally understood that all JVMs share and can quickly load the classes in them Good English friends can leave a message to help.
P. S Java inline function
Whether C + + is an inline function is up to you, and Java is up to the compiler. Inline function means that the function is expanded directly where it is called. When calling, the compiler does not need to press the parameters on the stack, and when returning, the parameters are out of the stack and resources are released, so as to improve the execution speed of the program. Java does not support direct declaration as inline function. If you want to inline it, it is the compiler has the final say, you can only request the compiler.
In addition to being overridden, final may also be inlined. If the function is private, it may also be inline.
Generally speaking, general functions will not be treated as inline functions. Only after final is declared, the compiler will consider whether to turn your function into an inline function.
Inlining is not necessarily good. When the method body specified as inlining is very large, the expansion cost may have exceeded the time of ordinary function calls. The introduction of inlining reduces the performance, because you should be more careful in selecting this keyword. However, in later versions of JVMs, optimization is made when processing inlining, It determines whether to expand the call based on the size of the method.
summary
The above is the invoke method of Java method introduced by Xiaobian. I hope it will be helpful to you. If you have any questions, please leave me a message, and Xiaobian will reply to you in time. Thank you very much for your support for the programming tips website!