Sponsored Link •
Phase two: Verification of symbolic references
Although phase one of verification likely takes place soon after the JVM loads a class file, phase two is delayed until the bytecodes contained in the class file actually are executed. Phase two verifies symbolic references. A symbolic reference is a character string that gives the name and possibly other information about the referenced item -- enough information to uniquely identify a class, field, or method. Thus, symbolic references to other classes give the full name of the class. Symbolic references to the fields of other classes give the class name, field name, and field descriptor. Symbolic references to the methods of other classes give the class name, method name, and method descriptor.
During phase two, the JVM follows the references from the class file being verified to the referenced class files, to make sure the references are correct. Because phase two has to look at other classes external to the class file being checked, phase two may require that new classes be loaded. Most JVM implementations will likely delay loading classes until they actually are used by the program.
If an implementation does load classes earlier -- perhaps in an attempt to speed up the loading process -- then it must still give the impression that it is loading classes as late as possible. If, for example, a Java virtual machine discovers during early loading that it can't find a certain referenced class, it doesn't throw a "class definition not found" error until (and unless) the referenced class is used for the first time by the running program.
Phase two and dynamic linking
Phase two of class-file verification is really just part of the process of dynamic linking. When a class file is loaded, it contains symbolic references to other classes and their fields and methods. Dynamic linking is the process of resolving symbolic references into direct references. As the JVM executes bytecodes and encounters an opcode that, for the first time, uses a symbolic reference to another class, the virtual machine must resolve the symbolic reference. The virtual machine performs two basic tasks during resolution:
The virtual machine remembers the direct reference so that if it encounters the same reference again later, it can immediately use the direct reference without needing to spend time resolving the symbolic reference again.
When the Java virtual machine resolves a symbolic reference, phase two of the class-file verifier makes sure the reference is valid. If the reference is not valid -- for instance, if the class cannot be loaded or if the class exists but doesn't contain the referenced field or method -- the class-file verifier throws an error.
As an example, consider a class named
Volcano. If a method
Volcano invokes a method in a class named
Lava, the name and descriptor of the method in
Lava are included as part of the binary data in the class
Volcano. So, during the course of execution when
Volcano's method first invokes
the JVM makes sure a method exists in class
Lava that has
a name and descriptor that matches those expected by class
Volcano. If the symbolic reference (class name, method
name, and descriptor) is correct, the virtual machine replaces it with
a direct reference, such as a pointer, which it will use from then on.
But if the symbolic reference from class
match any method in class
Lava, phase two verification
fails, and the JVM throws a "no such method" error.
The reason phase two of the class-file verifier must look at classes that refer to one another to make sure they are compatible is because Java programs are dynamically linked. Java compilers often will recompile classes that depend on a class you have changed, and in so doing, detect any incompatible changes at compile-time. But there may be times when your compiler doesn't recompile a dependent class. For example, if you are developing a large system, you will likely partition the various parts of the system into packages. If you compile each package separately, then a change to one class in a package would likely cause a recompilation of affected classes within that same package but not necessarily in any other package. Moreover, if you are using someone else's packages, especially if your program downloads class files from someone else's package across a network as it runs, it may be impossible for you to check for compatibility at compile-time. That's why phase two of the class-file verifier must check for compatibility at runtime.