We give a brief overview of the steps taken by the JVM to run a
program by connecting the various parts and operatons described
so far. We note that the JVM specifications are fairly flexible
and JVMs will do some steps in a different order.
Setup
The JVM starts by loading a class file, e.g. the class holding
the main() method of an application, as
described in Chapter 4:
Supplements: Class Loader. A rough schematic of how classes
definitions, object data, etc. are stored in the JVM is given in
Chapter 3: Supplements:
Objects and Classes in the JVM.
The loader verifies that the byte code and other aspects
of the class description is legitimate as described in Chapter
4: Supplements: Class File Verifier.
A preparation step deals with various housekeeping tasks
such as allocating memory for the class (i.e. static) variables.
The resolution means replacing the symbolic references in
the constant pool
with actual memory references. For every class and interface, the
JVM maintains an internal version of the constant pool obtained
from the class file called the runtime constant pool. The
references to other classes in this pool get resolved, i.e. linked,
either in the initialization phase (early resolution) or
when they are first encountered during the execution of the methods
and constructors (late resolution).
Initialization gives the class (i.e. static) variables their
appropriate values.
As we described in Chapter
3: Supplements: Objects and Classes ..., the JVM uses a method
area in memory to hold all the class type information. A heap
area holds data for objects as they are created. The garbage
collector periodically scans the heap to check for objects that
are no longer referenced and removes those that are not.
The class is now ready to be used for instantiation (as with a
new operator) and the methods executed.
The JVM uses daemon threads (those that live as long as there
are any non-daemon threads alive) to run internal processes such
as the garbage collector. A non-daemon
thread, for example, is created to run the main()
in the program specified .
Execution
Most modern processors rely heavily on registers in their architecture.
Registers are super fast memory words that hold instructions, pointers
to instructions, operand data for an instruction, pointers to data,
instruction counters, status registers (e.g. to indicate in execution
of an instruction caused an error state.), etc.
The JVM, however, uses a minimal number of registers. It was designed
to run on a broad range of processors from simple embedded systems
to mainframes and so took a lowest common denominator approach.
The JVM does use a PC (Program Counter) register for each thread
to keep track of which instruction it should execute next but instead
of relying on registers to hold intermediate data, the JVM uses
a stack (Last-In-First-Out) approach. When a method is invoked,
a frame is loaded on the stack. The frame holds the local
data for a method. If that method invokes another method, the current
method data is saved in its frame on the stack and the new frame
loaded on top of the stack. The processor runs the new method until
it either finishes or invokes another method. When the method finishes,
it is unloaded from the stack and the previous method continues.
(It can obtain data returned from the invoked method.)
See the instructions as described in Chapter
2: Supplements: JVM Instruction Set 1 and Chapter
5: Supplements: JVM Instruction Set 2 for a flavor of how the
methods in the JVM carry out their operations.
There is, of course, a great deal more detail to the JVM. (See
the references.) But
this should give a rough idea of what happens when a Java program
runs.
References & Web Resources
Most recent update: Oct.5, 2005
|