The Artima Developer Community
Sponsored Link

Chapter 8 of Inside the Java Virtual Machine
The Linking Model
by Bill Venners

<<  Page 14 of 20  >>

Advertisement

Example: The Linking of the Salutation Application

As an example of Java's linking model, consider the Salutation application shown below:

// On CD-ROM in file linking/ex5/Salutation.java
class Salutation {

    private static final String hello = "Hello, world!";
    private static final String greeting = "Greetings, planet!";
    private static final String salutation = "Salutations, orb!";

    private static int choice = (int) (Math.random() * 2.99);

    public static void main(String[] args) {

        String s = hello;
        if (choice == 1) {
            s = greeting;
        }
        else if (choice == 2) {
            s = salutation;
        }

        System.out.println(s);
    }
}

Assume that you have asked a Java virtual machine to run Salutation. When the virtual machine starts, it attempts to invoke the main() method of Salutation. It quickly realizes, however, that it can't invoke main(). The invocation of a method declared in a class is an active use of that class, which is not allowed until the class is initialized. Thus, before the virtual machine can invoke main(), it must initialize Salutation. And before it can initialize Salutation, it must load and link Salutation. So, the virtual machine hands the fully qualified name of Salutation to the bootstrap class loader, which retrieves the binary form of the class, parses the binary data into internal data structures, and creates an instance of java.lang.Class. The constant pool for Salutation is shown in Table 8-1.

Index Type Value
1 CONSTANT_String_info 30
2 CONSTANT_String_info 31
3 CONSTANT_String_info 39
4 CONSTANT_Class_info 37
5 CONSTANT_Class_info 44
6 CONSTANT_Class_info 45
7 CONSTANT_Class_info 46
8 CONSTANT_Class_info 47
9 CONSTANT_Methodref_info 7, 16
10 CONSTANT_Fieldref_info 4, 17
11 CONSTANT_Fieldref_info 8, 18
12 CONSTANT_Methodref_info 5, 19
13 CONSTANT_Methodref_info 6, 20
14 CONSTANT_Double_info 2.99
16 CONSTANT_NameAndType_info 26, 22
17 CONSTANT_NameAndType_info 41, 32
18 CONSTANT_NameAndType_info 49, 34
19 CONSTANT_NameAndType_info 50, 23
20 CONSTANT_NameAndType_info 51, 21
21 CONSTANT_Utf8_info "()D"
22 CONSTANT_Utf8_info "()V"
23 CONSTANT_Utf8_info "(Ljava/lang/String;)V"
24 CONSTANT_Utf8_info "([Ljava/lang/String;)V"
25 CONSTANT_Utf8_info "<clinit>"
26 CONSTANT_Utf8_info "<init>"
27 CONSTANT_Utf8_info "Code"
28 CONSTANT_Utf8_info "ConstantValue"
29 CONSTANT_Utf8_info "Exceptions"
30 CONSTANT_Utf8_info "Greetings, planet!"
31 CONSTANT_Utf8_info "Hello, world!"
32 CONSTANT_Utf8_info "I"
33 CONSTANT_Utf8_info "LineNumberTable"
34 CONSTANT_Utf8_info "Ljava/io/PrintStream;"
35 CONSTANT_Utf8_info "Ljava/lang/String;"
36 CONSTANT_Utf8_info "LocalVariables"
37 CONSTANT_Utf8_info "Salutation"
38 CONSTANT_Utf8_info "Salutation.java"
39 CONSTANT_Utf8_info "Salutations, orb!"
40 CONSTANT_Utf8_info "SourceFile"
41 CONSTANT_Utf8_info "choice"
42 CONSTANT_Utf8_info "greeting"
43 CONSTANT_Utf8_info "hello"
44 CONSTANT_Utf8_info "java/io/PrintStream"
45 CONSTANT_Utf8_info "java/lang/Math"
46 CONSTANT_Utf8_info "java/lang/Object"
47 CONSTANT_Utf8_info "java/lang/System"
48 CONSTANT_Utf8_info "main"
49 CONSTANT_Utf8_info "out"
50 CONSTANT_Utf8_info "println"
51 CONSTANT_Utf8_info "random"
52 CONSTANT_Utf8_info "salutation"

Table 8-1. Class Salutation's constant pool

As part of the loading process for Salutation, the Java virtual machine must make sure all of Salutation's superclasses have been loaded. To start this process, the virtual machine looks into Salutation's type data at the super_class item, which is a seven. The virtual machine looks up entry seven in the constant pool, and finds a CONSTANT_Class_info entry that serves as a symbolic reference to class java.lang.Object. See Figure 8-5 for a graphical depiction of this symbolic reference. The virtual machine resolves this symbolic reference, which causes it to load class Object. Because Object is the top of Salutation's inheritance hierarchy, the virtual machine and links and initializes Object as well.

Figure 8-5 The symbolic reference from Salutation to Object.

Figure 8-5 The symbolic reference from Salutation to Object.

Now that the Java virtual machine has loaded the Salutation class and loaded, linked and initialized all its superclasses, the virtual machine is ready to link Salutation. As the first step in the linking process, the virtual machine verifies the integrity of the binary representation of class Salutation. Assume this implementation of the Java virtual machine performs all verification up front, except for the verification of symbolic references. So by the time this official verification phase of linking is completed, the virtual machine will have verified:

  1. that Salutation's binary data is structurally correct
  2. that Salutation correctly implements the semantics of the Java language
  3. that Salutation's bytecodes won't crash the virtual machine

After the Java virtual machine has verified Salutation, it must prepare for Salutation's use by allocating any memory needed by the class. At this stage, the virtual machine allocates memory for Salutation's class variable, choice, and gives it a default initial value. Because the choice class variable is an int, it receives the default initial value of zero.

The three literal Strings--hello, greeting, and salutation--are constants, not class variables. They do not occupy memory space as class variables in the method area. They don't receive default initial values. Because they are declared static and final, they appear as CONSTANT_String_info entries in Salutation's constant pool. The constant pool for Salutation that was generated by javac is shown in Table 8-1. The entries that represent Salutation's constant strings are: for greeting, entry one; for hello, entry two; and for salutation, entry three.

After the processes of verification and preparation have successfully completed, the class is ready for resolution. As mentioned above, different implementations of the Java virtual machine may perform the resolution phase of linking at different times. Resolution of Salutation is optional at this point in its lifetime. Java virtual machines are not required to perform resolution until each symbolic reference is actually used by the program. If a symbolic reference is never actually used by a program, the virtual machine is not required to resolve it.

A Java virtual machine implementation could perform the recursive resolution process, described above for Salutation, at this point in the lifetime of a program. If so, the program would be completely linked before main() is ever invoked. A different Java virtual machine implementation could perform none of the resolution process at this point. Instead, it could resolve each symbolic reference the first time it is actually used by the running program. Other implementations could choose a resolution strategy between these two extremes. Although different implementations may perform resolution at different times, all implementations will ensure that a type is loaded, verified, prepared, and initialized before it is used.

Assume this implementation of the Java virtual machine uses late resolution. As each symbolic reference is used for the first time by the program, it will be checked for accuracy and converted into a direct reference. Assume also that this implementation uses the technique of replacing the opcode that refers to the constant pool with _quick equivalents.

Once this Java virtual machine implementation has loaded, verified, and prepared Salutation, it is ready to initialize it. As mentioned above, the Java virtual machine must initialize all superclasses of a class before it can initialize the class. In this case, the virtual machine has already initialized Object, the superclass of Salutation.

After the virtual machine has made sure all of Salutation's superclasses have been initialized (in this case, just class Object), it is ready to invoke Salutation's <clinit>() method. Because Salutation contains a class variable, width, that has an initializer that doesn't resolve at compile-time to a constant, the compiler does place a <clinit>() method into Salutation's class file.

Here's the <clinit>() method for Salutation:

             // Invoke class method Math.random(), passing no
             // parameters. Push double result.
 0 invokestatic #13 <Method double random()>
            // Push double constant 2.99 from constant pool.
 3 ldc2_w #14 <Double 2.99>
 6 dmul     // Pop two doubles, multiple, push double result.
 7 d2i      // Pop double, convert to int, push int result.
            // Pop int, store int Salutation.choice
 8 putstatic #10 <Field int choice>
11 return   // Return void from <clinit>()

The Java virtual machine executes Salutation's <clinit>() method to set the choice field to its proper initial value. Before executing <clinit>(), choice has its default initial value of zero. After executing <clinit>(), it has one of three values chosen pseudo-randomly: zero, one, or two.

The first instruction of the <clinit>() method, invokestatic #13, refers to constant pool entry 13, a CONSTANT_Methodref_info that represents a symbolic reference to the random() method of class java.lang.Math. You can see a graphical depiction of this symbolic reference in Figure 8-6. The Java virtual machine resolves this symbolic reference, which causes it to load, link, and initialize class java.lang.Math. It places a direct reference to the random() method into constant pool entry 13, marks the entry as resolved, and replaces the invokestatic opcode with invokestatic_quick.

Figure 8-6. The symbolic reference from Salutation to Math.random().

Figure 8-6. The symbolic reference from Salutation to Math.random().

Having completed the resolution process for constant pool entry 13, the Java virtual machine is ready to invoke the method. When the virtual machine actually invokes the random() method, it will load, link, and initialize any types referenced symbolically from Math's constant pool and random()'s code. When this method returns, the virtual machine will push the returned double value onto the main() method's operand stack.

To execute the next instruction, ldc2_w #14, the virtual machine looks into constant pool entry 14 and finds an unresolved CONSTANT_Double_info entry. The virtual machine resolves this entry to the double value 2.99, marks the entry as resolved, and replaces the ldc2_w opcode with ldc2_w_quick. Once the virtual machine has resolved constant pool entry 14, it pushes the constant double value, 2.99, onto the operand stack.

Note that this entry, a CONSTANT_Double_info, does not refer to any other constant pool entry or item outside this class. The eight bytes of the double value 2.99 are specified within the entry itself.

Note also that in this constant pool, there is no entry with an index of 15. As mentioned in Chapter 6, "The Java Class File," entries of type CONSTANT_Double_info and CONSTANT_Long_info occupy two slots in the constant pool. Thus, the CONSTANT_Double_info at index 14 is considered to occupy both indices 14 and 15.

To execute the next instruction, dmul, the virtual machine pops two doubles, multiplies them, and pushes the double result. For the next instruction, the virtual machine pops the double, converts it to int, and pushes the int result. Assume that for this particular execution of Salutation, the result of this operation is the int value two.

The next instruction, putstatic #10, uses another symbolic reference from the constant pool, this one to the choice variable of Salutation itself. This instruction illustrates that a class's bytecodes use symbolic references to refer not only to fields and methods of other types, but also to its own fields and methods. When the virtual machine executes this instruction, it looks up constant pool entry 10 and finds an as yet unresolved CONSTANT_Fieldref_info item. See Figure 8-7 For a graphical depiction of this symbolic reference. The virtual machine resolves the reference by locating the choice class variable in Salutation's type data in the method area, and placing a pointer to the actual variable data in constant pool entry 10. It marks the entry as resolved and replaces the putstatic opcode with putstatic_quick.

Figure 8-7. The symbolic reference from Salutation to its own choice field.

Figure 8-7. The symbolic reference from Salutation to its own choice field.

Once it has resolved the CONSTANT_Fieldref_info entry for choice, the virtual machine pops an int (in this case a two) from the operand stack and places it into the choice variable. The execution of the putstatic instruction is now complete.

Lastly, the virtual machine executes the return instruction, which signals to the virtual machine that the <clinit>() method, and hence the initialization of class Salutation, is complete. Now that class Salutation has been initialized, it is finally ready for use. The Java virtual machine invokes main(), and the program begins. Here's the bytecode sequence for Salutation's main() method:

                // Push objref to literal string from constant pool
                // entry 2
 0 ldc #2 <String "Hello, world!">
 2 astore_1     // Pop objref into loc var 1: String s = hello;
                // Push int from static field Salutation.choice. Note
                // that by this time, choice has definitely been
                // given its proper initial value.
 3 getstatic #10 <Field int choice>
 6 iconst_1     // Push int constant 1
                // Pop two ints, compare, if not equal branch to 16:
 7 if_icmpne 16 // if (choice == 1) {
                // Here, choice does equal 1. Push objref to string
                // literal from constant pool:
10 ldc #1 <String "Greetings, planet!">
12 astore_1     // Pop objref into loc var 1: s = greeting;
13 goto 26      // Branch unconditionally to offset 26
                // Push int from static field Salutation.choice
16 getstatic #10 <Field int choice>
19 iconst_2     // Push int constant 2
                // Pop two ints, compare, if not equal branch to 26:
20 if_icmpne 26 // if (choice == 2) {
                // Here, choice does equal 2. Push objref to string
                // literal from constant pool:
23 ldc #3 <String "Salutations, orb!">
25 astore_1     // Pop objref into loc var 1: String s = salutation;
                // Push objref from System.out
26 getstatic #11 <Field java.io.PrintStream out>
29 aload_1      // Push objref (to a String) from loc var 1
                // Pop objref (to a String), pop objref(to
                // System.out), invoke println() on System.out
                // passing the string as the only parameter:
                // System.out.println(s);
30 invokevirtual #12 <Method void println(java.lang.String)>
33 return       // Return void from main()

The first instruction in main(), ldc #2, uses a symbolic reference to the string literal "Hello, world!". When the virtual machine executes this instruction, it looks up constant pool entry two and finds a CONSTANT_String_info item that hasn't yet been resolved. See Figure 8-8 For a graphical depiction of the symbolic reference to this string literal.

Figure 8-8. A symbolic reference from Salutation to "Hello, World!"

Figure 8-8. A symbolic reference from Salutation to "Hello, world!"

As part of executing the ldc instruction, the virtual machine resolves the constant pool entry. It creates and interns a new String object with the value "Hello, world!", places a reference to the string object in the constant pool entry, marks the entry as resolved, and replaces the ldc opcode with an ldc_quick.

Now that the virtual machine has resolved the "Hello, world!" string literal, it pushes the reference to that String object onto the stack. The next instruction, astore_1, pops the reference and stores it into local variable position one, the s variable.

To execute the next instruction, getstatic #10, the virtual machine looks up constant pool entry 10 and discovers a CONSTANT_Fieldref_info entry that has already been resolved. This entry, a symbolic reference to Salutation's own choice field, was resolved by the putstatic #10 instruction in the <clinit>() method. The virtual machine simply replaces the getstatic opcode with getstatic_quick, and pushes the int value of choice onto the stack.

To execute main()'s next instruction, iconst_1, the virtual machine simply pushes int one onto the operand stack. For the next instruction, ificmpne 16, the virtual machine pops the top two ints and subtracts one from the other. In this case, since the value of choice was set by the <clinit>() method to be two, the result of the subtraction is not zero. As a consequence, the virtual machine takes the branch. It updates the pc register so that the next instruction it executes is the getstatic instruction at offset 16.

The getstatic instruction at offset 16 refers to the same constant pool entry referred to by the getstatic instruction at offset three: constant pool entry 10. When the virtual machine executes the getstatic at offset 16, it looks up constant pool entry 10 and finds a CONSTANT_Fieldref_info entry that is already resolved. It replaces the getstatic opcode with getstatic_quick, and pushes the int value of Salutation's choice class variable (a two) onto the operand stack.

To execute the next instruction, iconst_2, the virtual machine pushes an int two onto the stack. For the next instruction, another ificmpne 26, the virtual machine again pops two ints and subtracts one from the other. This time, however, both ints equal two, so the result of the subtraction is zero. As a consequence, the virtual machine does not take the branch and continues on to execute the next instruction in the bytecode array, another ldc.

The ldc instruction at offset 23 refers to constant pool entry three, a CONSTANT_String_info entry that serves as a symbolic reference to the string literal "Salutations, orb!". The virtual machine looks up this entry in the constant pool and discovers it is as yet unresolved. To resolve the entry, the virtual machine creates and interns a new String object with the value "Salutations, orb!", places a reference to the new object in the data for constant pool entry three, and replaces the ldc opcode with ldc_quick. Having resolved the string literal, the virtual machine pushes the reference to the String object onto the stack.

To execute the next instruction, astore_1, the virtual machine pops the object reference to the "Salutations, orb!" string literal off the stack and stores it into local variable slot one, overwriting the reference to "Hello, world!" written there by the astore_1 instruction at offset two.

The next instruction, getstatic #11, uses a symbolic reference to a public static class variable of java.lang.System with the name out and the type java.io.PrintStream. This symbolic reference occupies the CONSTANT_Fieldref_info entry at index 11 in the constant pool. See Figure 8-9 For a graphical depiction of this symbolic reference.

Figure 8-9. The symbolic reference from Salutation to System.out.

Figure 8-9. The symbolic reference from Salutation to System.out.

To resolve the reference to System.out, the Java virtual machine must load, link, and initialize java.lang.System to make sure it has a public static field, named out, of type java.io.PrintStream. Then, the virtual machine will replace the symbolic reference with a direct reference, such as a native pointer, so that any future uses of System.out by Saluation won't require resolution and will be faster. Lastly, the virtual machine will replace the getstatic opcode with getstatic_quick.

Once the virtual machine has successfully resolved the symbolic reference, it will push the reference to System.out onto the stack. To execute the next instruction, aload_1, the virtual machine simply pushes onto the stack the object reference from local variable one, which is the reference to the "Salutations, orb!" string literal.

To execute the next instruction, invokevirtual #12, the Java virtual machine looks up constant pool entry 12 and finds an unresolved CONSTANT_Methodref_info entry, a symbolic reference to the println() method of java.io.PrintStream. See Figure 8- 10 for a graphical depiction of this symbolic reference. The virtual machine loads, links, and initializes java.io.PrintStream, and makes sure it has a println() method that is public, returns void, and takes a String argument. It marks the entry as resolved and puts a direct reference (an index into PrintStream's method table) into the data for the resolved constant pool entry. Lastly, the virtual machine replaces the invokevirtual opcode with invokevirtual_quick, and places the method table index and the number of arguments accepted by the method as operands to the invokevirtual_quick opcode.

Figure 8-10. The symbolic reference from Salutation to PrintStream.println().

Figure 8-10. The symbolic reference from Salutation to PrintStream.println().

When the virtual machine actually invokes the println() method, it will load, link, and initialize any types referenced symbolically from PrintStream's constant pool and println()'s code.

The next instruction is the last instruction the main() method: return. Because main() was being executed by the only non- deamon thread running in the Salutation application, executing the return instruction will cause the virtual machine to exit. Note that constant pool entry one, which contained a symbolic reference to the "Greetings, planet!" string literal, was never resolved during this execution of the Salutation application. Because choice happened to be initialized with a value of two, the instruction that referred to constant pool entry one, the ldc #1 instruction at offset 10, was never executed. As a result, the virtual machine never created a String object with the value "Greetings, planet!".

<<  Page 14 of 20  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use