Source: https://book.huihoo.com/the-java-language-specification/first-edition/12.doc.html
Timestamp: 2019-04-23 20:32:29+00:00

Document:
This chapter specifies activities that occur during execution of a Java program. It is organized around the life cycle of a Java Virtual Machine and of the classes, interfaces, and objects that form a Java program.
The chapter continues with a specification of the procedures for creation of new class instances (§12.5); finalization of class instances (§12.6); and finalization of classes (§12.7). It concludes by describing the unloading of classes (§12.8) and the procedure followed when a virtual machine exits (§12.9).
The initial attempt to execute the method main of class Test discovers that the class Test is not loaded-that is, that the virtual machine does not currently contain a binary representation for this class. The virtual machine then uses a class loader (§20.14) to attempt to find such a binary representation. If this process fails, then an error is thrown. This loading process is described further in §12.2.
Verification checks that the loaded representation of Test is well-formed, with a proper symbol table. Verification also checks that the code that implements Test obeys the semantic requirements of Java and the Java Virtual Machine. If a problem is detected during verification, then an error is thrown. Verification is described further in §12.3.1.
Preparation involves allocation of static storage and any data structures that are used internally by the virtual machine, such as method tables. If a problem is detected during preparation, then an error is thrown. Preparation is described further in §12.3.2.
An implementation may instead choose to resolve a symbolic reference only when it is actively used; consistent use of this strategy for all symbolic references would represent the "laziest" form of resolution. In this case, if Test had several symbolic references to another class, then the references might be resolved one at a time, as they are used, or perhaps not at all, if these references were never used during execution of the program.
In our continuing example, the virtual machine is still trying to execute the method main of class Test. This is an attempted active use (§12.4.1) of the class, which is permitted only if the class has been initialized.
The binary format of a class or interface is normally the class file format described in The Java Virtual Machine, but other formats are possible, provided they meet the requirements specified in §13.1. The method defineClass (§20.14.3) of class ClassLoader may be used to construct Class objects from binary representations in the class file format.
A Java Virtual Machine system should maintain an internal table of classes and interfaces that have been loaded for the sake of resolving symbolic references. Each entry in the table should consist of a fully qualified class name (as a string), a class loader, and a Class object. Whenever a symbolic reference to a class or interface is to be resolved, a class loader is identified that is responsible for loading the class or interface, if necessary. The table should be consulted first, however; if it already contains an entry for that class name and class loader, then the class object in that entry should be used and no method of the class loader should be invoked. If the table contains no such entry, then the method loadClass (§20.14.2) of the class loader should be invoked, giving it the name of the class or interface. If and when it returns, the class object that it returns should be used to make a new entry in the table for that class name and class loader.
The purpose of this internal table is to allow the verification process (§12.3.1) to assume, for its purposes, that two classes or interfaces are the same if they have the same name and the same class loader. This property allows a class to be verified without loading all the classes and interfaces that it uses, whether actively or passively. Well-behaved class loaders do maintain this property: given the same name twice, a good class loader should return the same class object each time. But without the internal table, a malicious class loader could violate this property and undermine the security of the Java type system. A basic principle of the design of the Java language is that the type system cannot be subverted by code written in Java, not even by implementations of such otherwise sensitive system classes as ClassLoader (§20.14) and SecurityManager (§20.17).
An entry may be deleted from the internal table only after unloading (§12.8) the class or interface represented by the class object in the entry.
The loading process is implemented by the class ClassLoader (§20.14) and its subclasses. Different subclasses of ClassLoader may implement different loading policies. In particular, a class loader may cache binary representations of classes and interfaces, prefetch them based on expected usage, or load a group of related classes together. These activities may not be completely transparent to a running Java application if, for example, a newly compiled version of a class is not found because an older version is cached by a class loader. It is the responsibility of a class loader, however, to reflect loading errors only at points in the program they could have arisen without prefetching or group loading.
A cooperating class loader can enable a code generator to generate code for a group of class and interface types-perhaps an entire package-by loading the binary code for these types as a group. A format can be designed that allows all the internal symbolic references in such a group to be resolved, before the group is loaded. Such a strategy may also allow the generated code to be optimized before loading based on the known concrete types in the group. This approach may be useful in specific cases, but is discouraged as a general technique, since such a class file format is unlikely to be widely understood.
Linking is the process of taking a binary form of a class or interface type and combining it into the runtime state of the Java Virtual Machine, so that it can be executed. A class or interface type is always loaded before it is linked. Three different activities are involved in linking: verification, preparation, and resolution of symbolic references.
Java allows an implementation flexibility as to when linking activities (and, because of recursion, loading) take place, provided that the semantics of the language are respected, that a class or interface is completely verified and prepared before it is initialized, and that errors detected during linkage are thrown at a point in the program where some action is taken by the program that might require linkage to the class or interface involved in the error.
Verification ensures that the binary representation of a class or interface is structurally correct. For example, it checks that every instruction has a valid operation code; that every branch instruction branches to the start of some other instruction, rather than into the middle of an instruction; that every method is provided with a structurally correct signature; and that every instruction obeys the type discipline of the Java language.
For a more detailed description of the verification process, see the separate volume of this series, The Java Virtual Machine Specification.
Preparation involves creating the static fields (class variables and constants) for a class or interface and initializing such fields to the standard default values (§4.5.4). This does not require the execution of any Java code; explicit initializers for static fields are executed as part of initialization (§12.4), not preparation.
AbstractMethodError: A class that is not declared to be abstract has an abstract method. This can occur, for example, if a method that is originally not abstract is changed to be abstract after another class that inherits the now abstract method declaration has been compiled (§13.4.15).
If such an error is detected, then an instance of AbstractMethodError should be thrown at the point in the Java program that caused the class to be prepared.
A Java binary file references other classes and interfaces and their fields, methods, and constructors symbolically, using the fully-qualified names of the other classes and interfaces (§13.1). For fields and methods, these symbolic references include the name of the class or interface type that declares the field or method as well as the name of the field or method itself, together with appropriate type information.
Before a symbolic reference can be used it must be undergo resolution, wherein a symbolic reference is checked to be correct and, typically, replaced with a direct reference that can be more efficiently processed if the reference is used repeatedly.
IllegalAccessError: A symbolic reference has been encountered that specifies a use or assignment of a field, or invocation of a method, or creation of an instance of a class, to which the code containing the reference does not have access because the field or method was declared private, protected, or default access (not public), or because the class was not declared public. This can occur, for example, if a field that is originally declared public is changed to be private after another class that refers to the field has been compiled (§13.4.6).
InstantiationError: A symbolic reference has been encountered that is used in a class instance creation expression, but an instance cannot be created because the reference turns out to refer to an interface or to an abstract class. This can occur, for example, if a class that is originally not abstract is changed to be abstract after another class that refers to the class in question has been compiled (§13.4.1).
NoSuchFieldError: A symbolic reference has been encountered that refers to a specific field of a specific class or interface, but the class or interface does not declare a field of that name (it is specifically not sufficient for it simply to be an inherited field of that class or interface). This can occur, for example, if a field declaration was deleted from a class after another class that refers to the field was compiled (§13.4.7).
NoSuchMethodError: A symbolic reference has been encountered that refers to a specific method of a specific class or interface, but the class or interface does not declare a method of that signature (it is specifically not sufficient for it simply to be an inherited method of that class or interface). This can occur, for example, if a method declaration was deleted from a class after another class that refers to the method was compiled (§13.4.12).
Additionally, an UnsatisfiedLinkError (a subclass of LinkageError) may be thrown if a class declares a native method for which no implementation can be found. The error will occur if the method is used, or earlier depending on what kind of resolution strategy is being used by the virtual machine (§12.3).
The symbolic references within a group of types may be resolved even before the group is loaded (§12.2.2), in an implementation that uses a special (non-standard) binary format (§13.1). This corresponds to the traditional practice of "linkage editing." Even if this is not done, a Java implementation has a lot of flexibility. It may resolve all symbolic references from a type at the point of the first linkage activity on the type, or defer the resolution of each symbolic reference to the first use of that reference.
We note that the flexibility accorded the Java implementation in the linkage process does not affect correctly formed Java programs, which should never encounter linkage errors.
Before a class is initialized, its superclass must be initialized, but interfaces implemented by the class need not be initialized. Similarly, the superinterfaces of an interface need not be initialized before the interface is initialized.
T is a class and a method actually declared in T (rather than inherited from a superclass) is invoked.
T is a class and a constructor for class T is invoked, or U is an array with element type T, and an array of type U is created.
A non-constant field declared in T (rather than inherited from a superclass or superinterface) is used or assigned. A constant field is one that is (explicitly or implicitly) both final and static, and that is initialized with the value of a compile-time constant expression (§15.27). Java specifies that a reference to a constant field must be resolved at compile time to a copy of the compile-time constant value, so uses of such a field are never active uses. See §13.4.8 for a further discussion.
All other uses of a type are passive uses.
The intent here is that a class or interface type has a set of initializers that put it in a consistent state, and that this state is the first state that is observed by other classes. The static initializers and class variable initializers are executed in textual order, and may not refer to class variables declared in the class whose declarations appear textually after the use, even though these class variables are in scope (§8.5). This restriction is designed to detect, at compile time, most circular or otherwise malformed initializations.
As shown in an example in §8.5, the fact that initialization code is unrestricted allows examples to be constructed where the value of a class variable can be observed when it still has its initial default value, before its initializing expression is evaluated, but such examples are rare in practice. (Such examples can be also constructed for instance variable initialization; see the example at the end of §12.5). Java provides the full power of the language in these initializers; programmers must exercise some care. This power places an extra burden on code generators, but this burden would arise in any case because Java is concurrent (§12.4.3).
because the class Sub is never initialized; the reference to Sub.taxi is a reference to a field actually declared in class Super and is not an active use of the class Sub.
The reference to J.i is to a field that is a compile-time constant; therefore, it does not cause I to be initialized. The reference to K.j is a reference to a field actually declared in interface J that is not a compile-time constant; this causes initialization of the fields of interface J, but not those of its superinterface I, nor those of interface K. Despite the fact that the name K is used to refer to field j of interface J, interface K is not actively used.
Synchronize (§14.17) on the Class object that represents the class or interface to be initialized. This involves waiting until the current thread can obtain the lock for that object (§17.13).
If initialization is in progress for the class or interface by some other thread, then wait (§20.1.6) on this Class object (which temporarily releases the lock). When the current thread awakens from the wait, repeat this step.
Next, if the Class object represents a class rather than an interface, and the superclass of this class has not yet been initialized, then recursively perform this entire procedure for the superclass. If necessary, verify and prepare the superclass first. If the initialization of the superclass completes abruptly because of a thrown exception, then lock this Class object, label it erroneous, notify all waiting threads (§20.1.10), release the lock, and complete abruptly, throwing the same exception that resulted from initializing the superclass.
If the execution of the initializers completes normally, then lock this Class object, label it fully initialized, notify all waiting threads (§20.1.10), release the lock, and complete this procedure normally.
Lock the Class object, label it erroneous, notify all waiting threads (§20.1.10), release the lock, and complete this procedure abruptly with reason E or its replacement as determined in the previous step.
Compile-time analysis may, in some cases, be able to eliminate many of the checks that a type has been initialized from the generated code, if an initialization order for a group of related types can be determined. Such analysis must, however, fully account for the fact that Java is concurrent and that initialization code is unrestricted.
Evaluation of a class instance creation expression (§15.8) creates a new instance of the class whose name appears in the expression.
Invocation of the newInstance method (§20.3.6) of class Class creates a new instance of the class represented by the Class object for which the method was invoked.
Execution of a string concatenation operator (§15.17.1) that is not part of a constant expression sometimes creates a new String object to represent the result. String concatenation operators may also create temporary wrapper objects for a value of a primitive type.
This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with a explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.
Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.
See §8.6 for more details on constructor declarations.
The class Object has a protected method called finalize (§20.1.11); this method can be overridden by other classes. The particular definition of finalize that can be invoked for an object is called the finalizer of that object. Before the storage for an object is reclaimed by the garbage collector, the Java Virtual Machine will invoke the finalizer of that object.
Finalizers provide a chance to free up resources (such as file descriptors or operating system graphics contexts) that cannot be freed automatically by an automatic storage manager. In such situations, simply reclaiming the memory used by an object would not guarantee that the resources it held would be reclaimed.
The Java language does not specify how soon a finalizer will be invoked, except to say that it will happen before the storage for the object is reused. Also, the Java language does not specify which thread will invoke the finalizer for any given object. If an uncaught exception is thrown during the finalization, the exception is ignored and finalization of that object terminates.
Every object can be characterized by two attributes: it may be reachable, finalizer- reachable, or unreachable, and it may also be unfinalized, finalizable, or finalized.
A reachable object is any object that can be accessed in any potential continuing computation from any live thread. Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would naively be considered reachable. For example, a compiler or code generator may choose, explicitly or implicitly, to set a variable or parameter that will no longer be used to null to cause the storage for such an object to be potentially reclaimable sooner. A finalizer-reachable object can be reached from some finalizable object through some chain of references, but not from any live thread. An unreachable object cannot be reached by either means.
A finalizable object cannot also be unreachable; it can be reached because its finalizer may eventually be invoked, whereupon the thread running the finalizer will have access to the object, as this (§15.7.2). Thus, there are actually only eight possible states for an object.
If a class does not override method finalize of class Object (or overrides it in only a trivial way, as described above), then if instances of such as class become unreachable, they may be discarded immediately rather than made to await a second determination that they have become unreachable. This strategy is indicated by the dashed arrow (O) in the transition diagram.
Java programmers should also be aware that a finalizer can be automatically invoked, even though it is reachable, during finalization-on-exit (§12.9); moreover, a finalizer can also be invoked explicitly as an ordinary method. Therefore, we recommend that the design of finalize methods be kept simple and that they be programmed defensively, so that they will work in all cases.
Java imposes no ordering on finalize method calls. Finalizers may be called in any order, or even concurrently.
It is straightforward to implement a Java class that will cause a set of finalizer-like methods to be invoked in a specified order for a set of objects when all the objects become unreachable. Defining such a class is left as an exercise for the reader.
then this method will be invoked before the class is unloaded (§12.8). Like the finalize method for objects, this method will be automatically invoked only once. This method may optionally be declared private, protected, or public.
A Java Virtual Machine may provide mechanisms whereby classes are unloaded. The details of such mechanisms are not specified in this version of the Java Language Specification. In general, groups of related class and interface types will be unloaded together. This can be used, for example, to unload a group of related types that have been loaded using a particular class loader. Such a group might consist of all the classes implementing a single applet in a Java-based browser such as HotJava, for example.
A class may not be unloaded while any instance of it is still reachable (§12.6). A class or interface may not be unloaded while the Class object that represents it is still reachable.
Classes that declare class finalizers (§12.7) will have these finalizers run before they are unloaded.
All the threads that are not daemon threads (§20.20.24) terminate.
Some thread invokes the exit method (§20.16.2) of class Runtime or class System and the exit operation is not forbidden by the security manager (§20.17.13).
A Java program can specify that the finalizers of all objects that have finalizers, and all classes that have class finalizers, that have not yet been automatically invoked are to be run before the virtual machine exits. This is done by invoking the method runFinalizersOnExit of class System with the argument true. The default is to not run finalizers on exit, and this behavior may be restored by invoking runFinalizersOnExit with the argument false. An invocation of the runFinalizersOnExit method is permitted only if the caller is allowed to exit, and is otherwise rejected by the SecurityManager (§20.17).

References: §12
 §12
 §12
 §13
 §13
 §8
 §12
 §8