To put it simply: a symbolic reference is a string that contains enough information to be used in order to find the appropriate location. You say a symbolic reference to a method, such as: "Java/io/printstream.println: (ljava/lang/string;) V". There are classes of information, method names, method parameters and other information.
When you run the first time, you search for the method in the method table of the class, based on the contents of the string. Once run, the symbol reference is replaced with a direct reference, and the next time you do not have to search. The direct reference is the offset, and the offset virtual machine can find the starting position of the method bytecode directly in the memory area of the class. Ext.: Rednaxelafx
Links: http://www.zhihu.com/question/30300585/answer/51335493
Source: Know
Look at the "symbol reference" in the class file first.
Consider such a Java class:
public class X { public void foo() { bar(); } public void bar() { }}
The text representation of the class file it compiles is as follows:
Classfile/private/tmp/x.class Last modified June 13, 2015; Size 372 bytes MD5 checksum 8abb9cbb66266e8bc3f5eeb35c3cc4dd Compiled from ' X.java ' public class X sourcefile: ' X.java ' Minor version:0 Major version:51 Flags:acc_public, acc_superconstant pool: #1 = MethodRef #4. #16 Java/lang/object. " <init>:() v #2 = methodref #3. #17//X.bar: () v #3 = Class #18//X #4 = Class #19//Java/lang/object #5 = Utf8 <init> #6 = Utf8 () V #7 = Utf8 Code #8 = Utf8 linenumbertable #9 = Utf8 Localvariabletab Le #10 = Utf8 this #11 = Utf8 LX; #12 = Utf8 Foo #13 = Utf8 Bar #14 = Utf8 sourcefile #15 = Utf8 X.java #16 = Nameandtype #5: #6//"<init>":() V #17 = Nameandtype #13: #6//bar: () V #18 = Utf8 x #19 = Utf8 java/lang/object{public x (); Flags:acc_public code:stack=1, Locals=1, args_size=1 0:aload_0 1:invokespecial #1 Method java/lang/object. " <init> ":() V 4:return linenumbertable:line 1:0 Localvariabletable:start Length Slot Name Signature 0 5 0 this LX; public void foo (); Flags:acc_public code:stack=1, Locals=1, args_size=1 0:aload_0 1:invokevirtual #2 Method bar: () V 4:return linenumbertable:line 3:0 line 4:4 Local Variabletable:start Length Slot Name Signature 0 5 0 this LX; public void Bar (); Flags:acc_public code:stack=0, Locals=1, args_size=1 0:return linenumbertable:line 6:0 localvariabletable:Start Length Slot Name Signature 0 1 0 this LX;}
You can see that there is a section in the class file called "Constant Pool", which stores the contents of most constants in the class file.
To examine a bytecode directive in the Foo () method:
1: invokevirtual #2 // Method bar:()V
The actual encoding in the class file is:
[B6] [00 02]
Where 0xb6 is the opcode (opcode) of the invokevirtual instruction, the subsequent 0x0002 is the operand of the instruction (operand), which specifies the target method to invoke.
This parameter is the subscript for the constant pool in the class file. Then find the constant pool entry labeled 2, which is:
#2 = Methodref #3.#17 // X.bar:()V
The actual encoding in the class file is (in hexadecimal notation, the class file uses a high position in the first byte order (Big-endian)):
[0A] [00 03] [00 11]
Where 0x0a is the tag of Constant_methodref_info, the following 0x0003 and 0x0011 are the two parts of the constant pool entry: Class_index and Name_and_type_index. These two parts are constant pool subscripts, referencing the other two constant pool entries.
Follow this lead to find the constant pool entries that can be passed to the reference, and you'll see (in depth precedence):
#2 = Methodref #3.#17 // X.bar:()V #3 = Class #18 // X #18 = Utf8 X #17 = NameAndType #13:#6 // bar:()V #13 = Utf8 bar #6 = Utf8 ()V
To draw a reference relationship into a tree:
#2 Methodref X.bar:()V / #3 Class X #17 NameAndType bar:()V | / #18 Utf8 X #13 Utf8 bar #6 Utf8 ()V
A constant pool item labeled Utf8 is actually constant_utf8_info in the class file, which is a UTF-8 encoded string literal that has been slightly modified.
That's clear, isn't it?
It can be seen that the operand of the invokevirtual instruction in the class file passes through several layers of indirection and is finally represented by a string. This is what's in the class file.
"Symbol Reference"Real state: A string with a type (tag)/structure (a hierarchy of references between symbols).
==================================================
Then look at the "direct references" in the JVM.
This is not an example of a hotspot VM, as it is slightly more complex to implement. Let's look at a simpler implementation of Sun's Ganso Jvm--sun JDK 1.0.2 on the 32-bit x86 approach.
Please refer to the part of the Sun Classic vm in another answer: why is the address of the BS virtual function table (int*) (&BS) not the same as the virtual function address (int*) * (int*) (&BS)? -Rednaxelafx's answer
Sun Classic VM: (Take the example of a 32-bit sun JDK 1.0.2 on x86)
HObject ClassObject -4 [ hdr ]--> +0 [ obj ] --> +0 [ ... fields ... ] +4 [ methods ] \ methodtable ClassClass > +0 [ classdescriptor ] --> +0 [ ... ] +4 [ vtable[0] ] methodblock +8 [ vtable[1] ] --> +0 [ ... ] ... [ vtable... ]
(Please read the section on virtual method tables and JVMs in the links above.) Sun's Ganso JVM is also a virtual method table. )
The Ganso JVM parses the various parts of the class file (parse) into the internal data structure of the JVM when it does the load. For example, the metadata of a class is recorded in the Classclass structure, and the metadata for each method is recorded in the respective methodblock structure, and so on.
When a class is loaded, the Chang in the class file and the bytecode of each method (the Code property) are copied to the memory in the same way, which means that it is still in the state of "symbolic reference", and will be parsed (resolve) as a direct reference until it is actually used.
Suppose we are going to execute the invokevirtual instruction for the first time to call the bar () method in the Foo () method.
At this point the JVM will find that the directive has not been parsed (resolve), so it will parse it first.
By the constant pool subscript 0x0002 recorded by its operand, find the constant pool item # #, and find that the constant pool item has not yet been parsed (resolve), so go further and parse it.
The class name is found through the Class_index recorded by MethodRef, and the classclass structure of the class of the called method is further found, and then the method name and method descriptor are found by Name_and_type_index. Find the matching methodblock in the list of methods recorded on the CLASSCLASS structure, and finally write the methodblock of the found pointer back to the constant pool item #.
In other words, the content of the constant pool item # # In the run-time pool after class loading is consistent with the class file:
[00 03] [00 11]
(tag is placed elsewhere; small details: The data is still stored as high in the first byte order when it is loaded)
After parsing, assuming that the found methodblock* is 0x45762300, then the contents of the constant pool entry will change to:
[00 23 76 45]
(after parsing the byte order using the x86 primitive used in the pre-byte order (Little-endian), for subsequent ease of use)
In this way, when you query the constant pool item # # #, the inside is no longer a symbolic reference, but a methodblock* that can directly find the Java method meta-data. The methodblock* here is a
"Direct Reference"。
Parse the constant pool entry after # # # to return to the parsing of the invokevirtual directive.
In retrospect, the content of the instruction before parsing is:
[B6] [00 02]
After parsing, this code is rewritten as:
[D6] [06] [01]
where the opcode section is rewritten from invokevirtual to Invokevirtual_quick to indicate that the directive has been parsed.
The 2-byte space that originally stored the operand now has 2 1-byte information, the first of which is
Subscript for Virtual method table (vtable index), the second is the number of arguments to the method. Both of these messages are read by the methodblock* that were previously parsed by the constant pool entry.
That is
invokevirtual_quick vtable_index=6, args_size=1
In this example, Class X corresponds to the virtual method table in the JVM, which looks like this:
[0]: java.lang.Object.hashCode:()I[1]: java.lang.Object.equals:(Ljava/lang/Object;)Z[2]: java.lang.Object.clone:()Ljava/lang/Object;[3]: java.lang.Object.toString:()Ljava/lang/String;[4]: java.lang.Object.finalize:()V[5]: X.foo:()V[6]: X.bar:()V
So when the JVM executes Invokevirtual_quick to invoke X.bar (), it can find the actual target that should be called and then call past as long as it finds the virtual method table along the object reference and then takes the 6th item out of the methodblock*.
If Class X also has subclass Y, and Y writes the bar () method, then the virtual method table of class Y will look like this:
[0]: java.lang.Object.hashCode:()I[1]: java.lang.Object.equals:(Ljava/lang/Object;)Z[2]: java.lang.Object.clone:()Ljava/lang/Object;[3]: java.lang.Object.toString:()Ljava/lang/String;[4]: java.lang.Object.finalize:()V[5]: X.foo:()V[6]: Y.bar:()V
The bar () method implemented by class Y can then be found through vtable_index=6.
So in the Invokevirtual_quick command after parsing/rewriting,
virtual method Table subscript (vtable index)is also a
"Direct Reference"'s performance.
For the design of this "_quick" directive, refer to the ancient JVM specification in the 1th edition of Chapter 9th. Here is a copy:http://www. cs.miami.edu/~burt/reference/java/language_vm_specification.pdf
In the current hotspot VM, the resolution around the constant pool, invokevirtual (again emphasizing resolve) is not the same as the Ganso JVM, but the general idea is still the same.
The HotSpot VM's run-time pool has constantpool and constantpoolcache, and some types of constant pool entries are parsed directly in Constantpool, while others place the parsed results in Constantpoolcache. Previously sent a simple diagram example, you can refer to: Excuse me, JVM implementation read class file constant pool information is what?
==================================================
Thus, a symbolic reference is usually a design string-a textual representation of a reference relationship.
A direct reference is a form that the JVM (or other runtime environment) can use directly. It can be represented as a direct pointer (such as the constant pool item above is resolved to methodblock*), or it may be other forms (such as the vtable index in the invokevirtual_quick Directive).
The key point is not whether the form is a "direct pointer", but whether the JVM can "directly use" this form of data.
Java symbolic references and direct references