티스토리 뷰

프로그래밍

2.17 Execution

앙망 2010. 12. 8. 19:03
java.sun.com/docs/books/jvms/second_edition/html/Concepts.doc.html#19042


The JavaTM Virtual Machine Specification

Second Edition

Tim Lindholm
Frank Yellin


2.17 Execution

This section specifies activities that occur during execution of a program. It is organized around the life cycle of the Java virtual machine and of the classes, interfaces, and objects that form a program. It specifies the detailed procedures used in starting up the virtual machine (§2.17.1), class and interface type loading (§2.17.2), linking (§2.17.3), and initialization (§2.17.4). It then specifies the procedures for creation of new class instances (§2.17.6). It concludes by describing the unloading of classes (§2.17.8) and the procedure followed when a virtual machine exits (§2.17.9).

2.17.1 Virtual Machine Start-up

The Java virtual machine starts execution by invoking the method main of some specified class and passing it a single argument, which is an array of strings. This causes the specified class to be loaded (§2.17.2), linked (§2.17.3) to other types that it uses, and initialized (§2.17.4). The method main must be declared public, static, and void.

JVM 은 몇몇 명확한 클래스의 main 메소드를 부르고 문자열 배열의 단일 인자를 넘겨주어 실행을 시작한다. 이것은 명확한 클래스가 로드되고, 다른 타입에 링크되고, 초기화 하도록 한다. 이 main 메소드는 반드시 public, static, void 로 선언되어야 한다.

The manner in which the initial class is specified to the Java virtual machine is beyond the scope of this specification, but it is typical, in host environments that use command lines, for the fully qualified name of the class to be specified as a command-line argument and for subsequent command-line arguments to be used as strings to be provided as the argument to the method main. For example, using Sun's Java 2 SDK for Solaris, the command line

   java Terminator Hasta la vista Baby!

will start a Java virtual machine by invoking the method main of class Terminator (a class in an unnamed package) and passing it an array containing the four strings "Hasta", "la", "vista", and "Baby!".

We now outline the steps the virtual machine may take to execute Terminator, as an example of the loading, linking, and initialization processes that are described further in later sections.

The initial attempt to execute the method main of class Terminator discovers that the class Terminator is not loaded-that is, the virtual machine does not currently contain a binary representation for this class. The virtual machine then uses a ClassLoader (§2.17.2) to attempt to find such a binary representation. If this process fails, an error is thrown. This loading process is described further in (§2.17.2).

Terminator 클래스의 main 메소드를 실행하는 최초 시도에서는 아직 클래스가 JVM 에 로드되지 않았다. 즉 클래스의 바이너리를 가지고 있지 않기 때문에 ClassLoader를 사용해서 가져온다. 만약 이 과정이 실패하면 에러가 발생된다.

After Terminator is loaded, it must be initialized before main can be invoked, and a type (class or interface) must always be linked before it is initialized. Linking (§2.17.3) involves verification, preparation, and (optionally) resolution.

Terminator 가 로드된 후, main 이 호출되기 전에 초기화되고 타입(클래스나 인터페이스)은 항상 초기화 전에 링크되어야 한다.  Linking은 검증, 준비, (선택으로) 해결을 포함한다.

Verification (§2.17.3) checks that the loaded representation of Terminator is well formed, with a proper symbol table. Verification also checks that the code that implements Terminator obeys the semantic requirements of the Java virtual machine. If a problem is detected during verification, an error is thrown.

검증(verification)은 로드된 Terminator의 representation이 제대로 형성되었는지 적절한 심볼 테이블을 가지고 검사한다. 또한 코드검사도 한다. 검사중 문제가 발견되면 에러를 발생한다.

Preparation (§2.17.3) involves allocation of static storage and any data structures that are used internally by the virtual machine, such as method tables.

준비 : 정적인 저장소와 가상먼신에서 내부적으로 사용되는 데이타 구조들(예를 들어 메소드 테이블)을  할당

Resolution (§2.17.3) is the process of checking symbolic references from class Terminator to other classes and interfaces, by loading the other classes and interfaces that are mentioned and checking that the references are correct.

Resolution 에서는 클래스 Teminator에서 다른 클래스나 인터페이스에 대한 심볼릭 참조를 확인한다. - 언급된 다른 클래스와 인터페이스를 로딩하고 레퍼런스가 옳은지 확인하다.

The resolution step is optional at the time of initial linkage. An implementation may resolve a symbolic reference from a class or interface that is being linked very early, even to the point of resolving all symbolic references from the classes and interfaces that are further referenced, recursively. (This resolution may result in errors from further loading and linking steps.) This implementation choice represents one extreme and is similar to the kind of static linkage that has been done for many years in simple implementations of the C language.

resolution 단계는 초기 링크 시점에서 선택적이다.

An implementation may instead choose to resolve a symbolic reference only when it is actually used; consistent use of this strategy for all symbolic references would represent the "laziest" form of resolution. In this case, if Terminator had several symbolic references to another class, the references might be resolved one at a time or perhaps not at all, if these references were never used during execution of the program.

The only requirement regarding when resolution is performed is that any errors detected during resolution must be thrown at a point in the program where some action is taken by the program that might, directly or indirectly, require linkage to the class or interface involved in the error. In the "static" example implementation choice described earlier, loading and linking errors could occur before the program is executed if they involved a class or interface mentioned in the class Terminator or any of the further, recursively referenced classes and interfaces. In a system that implemented the "laziest" resolution, these errors would be thrown only when a symbolic reference was used.

In our running example, the virtual machine is still trying to execute the method main of class Terminator. This is permitted only if the class has been initialized (§2.17.4).

Initialization consists of execution of any class variable initializers and static initializers of the class Terminator, in textual order. But before Terminator can be initialized, its direct superclass must be initialized, as well as the direct superclass of its direct superclass, and so on, recursively. In the simplest case, Terminator has Object as its implicit direct superclass; if class Object has not yet been initialized, then it must be initialized before Terminator is initialized.

If class Terminator has another class Super as its superclass, then Super must be initialized before Terminator. This requires loading, verifying, and preparing Super, if this has not already been done, and, depending on the implementation, may also involve resolving the symbolic references from Super and so on, recursively.

Terminator 클래스를 초기하기 전에 모든 superclass 를 초기화해야 한다.

Initialization may thus cause loading, linking, and initialization errors, including such errors involving other types.
Finally, after completion of the initialization for class Terminator (during which other consequential loading, linking, and initializing may have occurred), the method main of Terminator is invoked.

클래스의 초기화가 완료된 후 main 메소드가 불려진다.

2.17.2 Loading
Loading refers to the process of finding the binary form of a class or interface type with a particular name, perhaps by computing it on the fly, but more typically by retrieving a binary representation previously computed from source code by a compiler and constructing, from that binary form, a Class object to represent the class or interface. The binary format of a class or interface is normally the class file format (see Chapter 4, "The class File Format").

Loading은 특정 이름을 가지고 클래스나 인터페이스의 바이너리 폼을 찾는 과정이다.

The loading process is implemented by the class ClassLoader 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 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 where they could have arisen without prefetching or group loading.

로딩 처리는 ClassLoader 클래스와 서브클래스들에 의해 수행된다.

If an error occurs during class loading, then an instance of one of the following subclasses of class LinkageError will be thrown at any point in the program that (directly or indirectly) uses the type:
  • ClassFormatError: The binary data that purports to specify a requested compiled class or interface is malformed.
  • UnsupportedClassVersionError: A class or interface could not be loaded because it is represented using an unsupported version of the class file format.3
  • ClassCircularityError: A class or interface could not be loaded because it would be its own superclass or superinterface (§2.13.2).
  • NoClassDefFoundError: No definition for a requested class or interface could be found by the relevant class loader.

2.17.3 Linking: Verification, Preparation, and Resolution

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.


The Java programming language 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.
For example, an implementation may choose to resolve each symbolic reference in a class or interface individually, only when it is used (lazy or late resolution), or to resolve them all at once, for example, while the class is being verified (static resolution). This means that the resolution process may continue, in some implementations, after a class or interface has been initialized.
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 programming language.
If an error occurs during verification, then an instance of the following subclass of class LinkageError will be thrown at the point in the program that caused the class to be verified:
  • VerifyError: The binary definition for a class or interface failed to pass a set of required checks to verify that it cannot violate the integrity of the Java virtual machine.

Preparation involves creating the static fields for a class or interface and initializing such fields to the standard default values (§2.5.1). This does not require the execution of any Java virtual machine code; explicit initializers for static fields are executed as part of initialization (§2.17.4), not preparation.
Implementations of the Java virtual machine may precompute additional data structures at preparation time in order to make later operations on a class or interface more efficient. One particularly useful data structure is a "method table" or other data structure that allows any method to be invoked on instances of a class without requiring a search of superclasses at invocation time.
The binary representation of a class or interface references other classes and interfaces and their fields, methods, and constructors symbolically, using the fully qualified names (§2.7.5) of the other classes and interfaces. 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 undergo resolution, wherein a symbolic reference is validated and, typically, replaced with a direct reference that can be more efficiently processed if the reference is used repeatedly.
If an error occurs during resolution, then an instance of one of the following subclasses of class IncompatibleClassChangeError, or of some other subclass, or of IncompatibleClassChangeError itself (which is a subclass of the class LinkageError) may be thrown at any point in the program that uses a symbolic reference to the type:
  • 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.
  • 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.
  • 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. This can occur, for example, if a field declaration was deleted from a class after another class that refers to the field was compiled.
  • 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 name and signature. This can occur, for example, if a method declaration was deleted from a class after another class that refers to the method was compiled.

2.17.4 Initialization

Initialization of a class consists of executing its static initializers (§2.11) and the initializers for static fields (§2.9.2) declared in the class. Initialization of an interface consists of executing the initializers for fields declared in the interface (§2.13.3.1).
Before a class or interface is initialized, its direct 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.
A class or interface type T will be initialized immediately before one of the following occurs:
  • T is a class and an instance of T is created.
  • T is a class and a static method of T is invoked.
  • A nonconstant static field of T 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. A reference to such a field must be resolved at compile time to a copy of the compile-time constant value, so uses of such a field never cause initialization.

Invocation of certain methods in library classes (§3.12) also causes class or interface initialization. See the Java 2 platform's class library specifications (for example, class Class and package java.lang.reflect) for details.
The intent here is that a type have a set of initializers that put it in a consistent state and that this state be 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. This restriction is designed to detect, at compile time, most circular or otherwise malformed initializations.
Before a class or interface is initialized its superclass is initialized, if it has not previously been initialized.

2.17.5 Detailed Initialization Procedure

Initialization of a class or interface requires careful synchronization, since some other thread may be trying to initialize the same class or interface at the same time. There is also the possibility that initialization of a class or interface may be requested recursively as part of the initialization of that class or interface; for example, a variable initializer in class A might invoke a method of an unrelated class B, which might in turn invoke a method of class A. The implementation of the Java virtual machine is responsible for taking care of synchronization and recursive initialization by using the following procedure. It assumes that the Class object has already been verified and prepared and that the Class object contains state that can indicate one of four situations:
  • This Class object is verified and prepared but not initialized.
  • This Class object is being initialized by some particular thread T.
  • This Class object is fully initialized and ready for use.
  • This Class object is in an erroneous state, perhaps because the verification step failed or because initialization was attempted and failed.

The procedure for initializing a class or interface is then as follows:
  1. Synchronize 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 (§8.13).
  2. If initialization by some other thread is in progress for the class or interface, then wait on this Class object (which temporarily releases the lock). When the current thread awakens from the wait, repeat this step.
  3. If initialization is in progress for the class or interface by the current thread, then this must be a recursive request for initialization. Release the lock on the Class object and complete normally.
  4. If the class or interface has already been initialized, then no further action is required. Release the lock on the Class object and complete normally.
  5. If the Class object is in an erroneous state, then initialization is not possible. Release the lock on the Class object and throw a NoClassDefFoundError.
  6. Otherwise, record the fact that initialization of the Class object is now in progress by the current thread and release the lock on the Class object.
  7. Next, if the Class object represents a class rather than an interface, and the direct superclass of this class has not yet been initialized, then recursively perform this entire procedure for the uninitialized superclass. If the initialization of the direct superclass completes abruptly because of a thrown exception, then lock this Class object, label it erroneous, notify all waiting threads, release the lock, and complete abruptly, throwing the same exception that resulted from the initializing the superclass.
  8. Next, execute either the class variable initializers and static initializers of the class or the field initializers of the interface, in textual order, as though they were a single block, except that final static variables and fields of interfaces whose values are compile-time constants are initialized first.
  9. If the execution of the initializers completes normally, then lock this Class object, label it fully initialized, notify all waiting threads, release the lock, and complete this procedure normally.
  10. Otherwise, the initializers must have completed abruptly by throwing some exception E. If the class of E is not Error or one of its subclasses, then create a new instance of the class ExceptionInInitializerError, with E as the argument, and use this object in place of E in the following step. But if a new instance of ExceptionInInitializerError cannot be created because an OutOfMemoryError occurs, then instead use an OutOfMemoryError object in place of E in the following step.
  11. Lock the Class object, label it erroneous, notify all waiting threads, release the lock, and complete this procedure abruptly with reason E or its replacement as determined in the previous step.

In some early implementations of the Java virtual machine, an exception during class initialization was ignored rather than allowing it to cause an ExceptionInInitializerError as described here.

2.17.6 Creation of New Class Instances

A new class instance is explicitly created when one of the following situations occurs:
  • Evaluation of a class instance creation expression creates a new instance of the class whose name appears in the expression.
  • Invocation of the newInstance method of class Class creates a new instance of the class represented by the Class object for which the method was invoked.

A new class instance may be implicitly created in the following situations:
  • Loading of a class or interface that contains a String literal may create a new String object (§2.4.8) to represent that literal. This may not occur if the a String object has already been created to represent a previous occurrence of that literal, or if the String.intern method has been invoked on a String object representing the same string as the literal.
  • Execution of a string concatenation operator 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 (§2.4.1).

Each of these situations identifies a particular constructor to be called with specified arguments (possibly none) as part of the class instance creation process.
Whenever a new class instance is created, memory space is allocated for it with room for all the instance variables declared in the class type and all the instance variables declared in each superclass of the class type, including all the instance variables that may be hidden. If there is not sufficient space available to allocate memory for the object, then creation of the class instance completes abruptly with an OutOfMemoryError. Otherwise, all the instance variables in the new object, including those declared in superclasses, are initialized to their default values (§2.5.1).
Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:
  1. Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.
  2. If this constructor begins with an explicit constructor invocation of another constructor in the same class (using this), then evaluate the arguments and process that 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 5.
  3. If this constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this) and is in a class other than Object, then this constructor will begin with an 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.
  4. Execute the instance variable initializers for this class, assigning their values to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5. (In some early implementations, the compiler incorrectly omitted the code to initialize a field if the field initializer expression was a constant expression whose value was equal to the default initialization value for its type. This was a bug.)
  5. 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.

Unlike C++, the Java programming language does not specify altered rules for method dispatch during the creation of a new class instance. If methods are invoked that are overridden in subclasses in the object being initialized, then these overriding methods are used, even before the new object is completely created.

2.17.7 Finalization of Class Instances

The class Object has a protected method called finalize; 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 programming 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. Nor does the language 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.
The finalize method declared in class Object takes no action. However, the fact that class Object declares a finalize method means that the finalize method for any class can always invoke the finalize method for its superclass, which is usually good practice. (Unlike constructors, finalizers do not automatically invoke the finalizer for the superclass; such an invocation must be coded explicitly.)
For efficiency, an implementation may keep track of classes that do not override the finalize method of class Object or that override it in a trivial way, such as
   protected void finalize() { super.finalize(); }

We encourage implementations to treat such objects as having a finalizer that is not overridden and to finalize them more efficiently.
The finalize method may be invoked explicitly, just like any other method. However, doing so does not have any effect on the object's eventual automatic finalization.
The Java virtual machine imposes no ordering on finalize method calls. Finalizers may be called in any order or even concurrently.
As an example, if a circularly linked group of unfinalized objects becomes unreachable, then all the objects may become finalizable together. Eventually, the finalizers for these objects may be invoked in any order or even concurrently using multiple threads. If the automatic storage manager later finds that the objects are unreachable, then their storage can be reclaimed.

2.17.8 Unloading of Classes and Interfaces

A class or interface may be unloaded if and only if its class loader is unreachable. The bootstrap class loader is always reachable; as a result, system classes may never be unloaded.

2.17.9 Virtual Machine Exit

The Java virtual machine terminates all its activity and exits when one of two things happens:
  • All the threads that are not daemon threads (§2.19) terminate.
  • Some thread invokes the exit method of class Runtime or class System, and the exit operation is permitted by the security manager.

A program can specify that all finalizers that have not been automatically invoked are to be run before the virtual machine exits. This is done by invoking the method runFinalizersOnExit of the class System with the argument true.4 By default finalizers are not run on exit. Once running finalizers on exit has been enabled it may be disabled 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 security manager.
댓글