Sponsored Link •
|
Advertisement
|
Salutation
ApplicationAs 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.
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:
Salutation
's binary data is structurally correct
Salutation
correctly implements the semantics of the Java language
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 String
s--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
.
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
double
s, 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
.
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.
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 int
s 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
int
s and subtracts one from the other. This time, however, both
int
s 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.
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.
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!"
.
Sponsored Links
|