| Home > Core Java FAQ
> Java Virtual
Machine FAQ |
| Java
Virtual Machine |
| |
|
Q .
What is Java VM?
|
Ans : Java Virtual Machine (VM) is a software simulation of a computer which executes Java class codes. This is the basic building blocks for running Java programs, and is embedded in all Java capable web browsers, as well as Java Runtime Environment (JRE).
You can download a Java Virtual Machine and its specification from
JavaSoft's web site.
|
|
Q .
Why do I need Java VM?
|
Ans
: You will need to have Java VM installed on your system only if you want to use Timecruiser enhanced features such as RSVP, Registration, and Timecruiser HTML or JavaScript editions.
These features use CGI scripts to invoke Java VM to process Timecruiser and related data.
|
|
Q .
Where can I download Java VM for my system?
|
Ans
: You can download JDK or JRE from the following sites. Java Download Sites Platform URL
|
|
Q . How do I install Java VM for my system?
|
Ans
: For Windows user, JavaSoft is kind enough to package the JDK and JRE with InstallShield, simply download and invoke the executable and the install program will guide you throughout the whole process.
For other platforms, please read the installation guide for each platform.
|
|
Q .
What is JRE?
|
Ans
: JRE stands for Java Runtime Environment. This is a package introduced by JavaSoft to deliver Java computing environment in a minimal packaging. And is freely redistributable by software vendors like us. The size of JRE for Windows is about 2.6MB, and it uses InstallShield to ease the installation process.
|
|
Q . What is the difference between Java VM and JRE?
|
Ans : Java VM is considered part of the packaging in JRE. Java VM is used as the computing platform for all Java programs. JRE is a redistributable package for Java computing. Before JRE is available, JDK is the only package that contains a Java
VM.
|
|
Q . Where can I download JRE for my system?
|
Ans
:
You can download JRE where you will find Java VM. Consult
"Where can I download Java VM for my
system?"
|
|
Q . How do I install JRE on my system?
|
Ans
:
The installation procedure is not different from that of JDK. Consult "How do I install Java VM on my system?" for
detail.
|
|
Q . What's the difference between web server Java extension and CGI invoked Java?
|
Ans
:
Not all web servers provide Java extension. If your web server supports Java extension, that would generally mean that your web server can run Java classes directly in its process space. This is the
fastest and most efficient way of using Java on the web server, although it is a bit restricted.
CGI invoked Java is simply using web server's existing gateway which uses a script to externally run Java classes. It is less efficient but there is no restriction to this method as well as web server
independent.
|
|
Q . How can a Java program determine the level of JDK support given by the underlying VM? I.e. is it running in a JDK 1.0.2 or 1.1
VM?
|
Ans
:
Look at the java.version system property with:
String ver = System.getProperty("java.version");
There isn't a lot of standardization on the string contents however. Another possibility is to try { ... } to load a class that is unique to
one release, like this:
boolean isJDK1_1 = true;
try {
// java.awt.Cursor is available only in the 1.1.x JDK
Class cls = Class.forName("java.awt.Cursor");
} catch (Exception e) {
// we should have written 'ClassNotFoundException e',
// but Communicator generates security exception instead.
isJDK1_1 = false;
}
This approach has the advantage that it can be compiled by any version compiler.
|
|
Q . When, and by whom, is the main method of a class invoked?
|
Ans
:
Normally,
you don't invoke main
yourself; the Java Virtual Machine invokes it for you if it uses
your class as its starting point of execution.
A
Java Virtual Machine always starts execution from the main
method of some class. Even a large Java application, such as the
HotJava™ browser, starts execution in one class's main
method. The main method must be declared as public
and static, it must have no return value, and it must
declare a String array as its sole parameter:
public static void main(String[] args) {
// ...
}
You can include
such a main method in any class you define. The Java
compiler does not complain about classes containing unused main
methods.
How you as a user start a Java
Virtual Machine can vary in different implementations. In some
implementations, such as the JDK (1.0.2 and 1.1) on Solaris and
Win32, you start the virtual machine with a command line that
specifies a class and an optional list of strings for your program
to start with. For example, suppose that your Java Virtual Machine
is stored in a command named java, and you have
written and compiled the following CountWords class:
public class CountWords {
public static void main(String[] args) {
System.out.println(args.length);
}
}
If you now issue
the command:
java CountWords argyle baldric corral delirium
the Java Virtual
Machine starts up by seeking and loading your CountWords
class, checking that it has an appropriate main
method, and invoking that main method with an array
of four strings ("argyle", "baldric",
"corral", and "delirium")
as its sole argument. In this example case, the main
method merely prints out the number of strings in the argument String
array.
Most classes are never meant to
provide the starting point for an application, but it is often
useful to include a main method in them anyway. You
can use main as a convenient way to test your class
repeatedly as you develop it. When you later integrate your class
into an application containing other classes, these extra main
methods are automatically ignored, except for the single class you
use as your application's starting point.
|
|
Q . What are bytecodes?
|
Ans
:
Java bytecodes are a language of
machine instructions understood by the Java Virtual Machine and
usually generated (compiled) from Java language source code.
Java
bytecodes encode instructions for the Java Virtual Machine. These
instructions specify both operations and operands, always in the
following format:
- opcode (1 byte): specifies what
operation to perform
- operands (0-n bytes): specify
data for the operation
The 1-byte
opcode is simply a number that the Java Virtual Machine interprets
as a specific instruction. For instance, opcode value 104 (0x68
in hexadecimal representation) is the instruction to multiply two int
values—it instructs the virtual machine to take the top two
values off the Java operand stack, multiply them, and push the
product back on the stack. In addition to its numerical value,
each opcode has a conventional short name, called its mnemonic.
The mnemonic for opcode 104 is imul.
Following each opcode come any
operands the opcode may require. The Java Virtual Machine uses the
opcode to determine just how many following bytes are operands for
that opcode. In some cases, such as the imul
instruction, the opcode's single byte represents the entire
instruction—no operands are needed. In other cases, the opcode
may require one or more operands. For example, the fload
instruction (opcode value 23, or 0x17 in hexadecimal)
loads a float value from a local variable, and it
uses an index operand to specify which local variable to load
from. If the local variable of interest is at index 5, the
complete instruction would have the following two bytes:
23 5 [fload from local variable at index 5]
There are
different approaches for learning more about Java Virtual Machine
instructions. If you want the comprehensive, detailed picture,
read The Java Virtual Machine Specification (Tim Lindholm
and Frank Yellin, 1996, Addison Wesley). It covers each virtual
machine instruction—its name (mnemonic), what it does, what
operands it takes, how it interacts with the operand stack, and
more. It's not light reading, but it's a wonderful resource when
you need it. You can also learn by experimentation, using javap
|
|
Q . What is javap?
|
Ans
:
The javap
program is a class file disassembler that comes with the JDK
(1.0.2 and 1.1).
The JDK (1.0.2 and
1.1) provides a Java class file disassembler, the javap
program, which can translate from binary opcodes and operands to a
human-readable format with opcode mnemonics, decimal numbers, and
so on. javap is a simple, useful tool for exploring
compiled class files and for learning about the Java Virtual
Machine instruction set.
The rest of this answer uses javap
to explore the bytecodes generated from the following Java source
file:
public class BytecodeExample {
public float multiply(byte a, short b, int c, float d) {
return a * b * c * d;
}
}
After
you compile this source file to a class file, run the javap
program on the resulting class (use the -c option to see the
virtual machine instructions):
javap -c ByteCodeExample
(Note that you
provide just the class name, not the full name of the
file containing the class.) The output from this command on the
JDK 1.1 includes the virtual machine instructions for the multiply
method:
Method float multiply(byte,short,int,float)
0 iload_1
1 iload_2
2 imul
3 iload_3
4 imul
5 i2f
6 fload 4
8 fmul
9 freturn
Following
are the same instructions, explained one by one; in each case the
stack referred to is the Java operand stack.
iload_1 |
load int
value of first method argument onto stack |
iload_2 |
load int
value of second method argument onto stack |
imul |
take two int
values from top of stack, multiply them, place product (int
value) on stack |
iload_3 |
load int
value of third method argument onto stack |
imul |
take two int
values from top of stack, multiply them, place product (int
value) on stack |
i2f |
convert int
value on top of stack to float value |
fload 4 |
load float
value of fourth method argument onto stack |
fmul |
take two float
values from top of stack, multiply them, place product (float
value) on stack |
freturn |
take float
value from top of stack, return it as value from this method |
To
appreciate the convenience provided by the javap
program, consider the same 10 bytes of the multiply
method in plain numerical (decimal) form:
27 28 104 29 104 134 23 4 106 174
|
|
Q . What does it mean to say that Java is interpreted?
|
Ans
:
Several
implementations of the Java Virtual Machine, including the JDK
(1.0.2 and 1.1), interpret compiled Java code (virtual machine
instructions)—for each instruction, the virtual machine carries
out the specified behavior before reading the next instruction.
Java
technology uses both compilation and interpretation. Compilation
is the process of translating content (such as code) from one
language to another, and storing the results of that translation
for later use. Many well-known programming languages, such as C,
Pascal, and Fortran, are usually compiled straight from source
code to native machine code. In contrast, the Java programming
language is compiled into Java class files, which contain
architecture-independent instructions for the Java Virtual Machine
Interpretation also involves
translating code from one language to another, except you directly
execute the translation instead of storing it. Languages (or
language families) such as Lisp and Basic are typically
interpreted straight from source code to execution. In many Java
implementations, interpretation picks up where compilation left
off. Java Virtual Machine instructions, which were compiled from
source code, are interpreted by the virtual machine—converted on
the fly into native machine code, which is then executed rather
than stored.
Interpreting virtual machine
instructions is common on existing implementations of the Java
Virtual Machine (such as the JDK 1.0.2 and 1.1), but is not
required by either The Java Language Specification or The
Java Virtual Machine Specification. A virtual machine
implementation may also use just-in-time (JIT) compilation:
translating virtual machine instructions into native machine code
at run time on the local platform, and then executing from that
stored machine code. (It is even possible to have hardware that
directly implements the Java Virtual Machine instruction set; such
machines would then run compiled Java code natively.)
Interpreted code is generally more
flexible and adaptive than compiled code, but you usually pay a
considerable price in execution speed because of the extra
translation work performed as the program executes. This point is
especially clear when you consider code with loops. The example
below shows Java source code for a simple loop to sum integers
from 1 to 1000:
int sum = 0;
for (int i = 1; i <= 1000; ++i) {
sum += i;
}
After compilation
into virtual machine instructions (and displayed by javap -c),
the loop looks like:
0 iconst_0
1 istore_1
2 iconst_1
3 istore_2
4 goto 14
7 iload_1
8 iload_2
9 iadd
10 istore_1
11 iinc 2 1
14 iload_2
15 sipush 1000
18 if_icmple 7
21 return
Using
just-in-time compilation, the above virtual machine instructions
can be translated once into native machine code, and then the
machine code is used directly each time through the loop. Using
interpretation, on the other hand, means that the machine code for
executing the loop is not stored for reuse—the virtual machine
instructions are read and translated into machine code each
time through the loop.
|
|
Q . What kind of garbage collection does the Java Virtual Machine use?
|
Ans
:
The Java
Virtual Machine specification does not require any particular
algorithm; the virtual machine implementation in the JDK 1.0.2 and
1.1 uses a partially conservative mark-and-sweep garbage
collector, but other implementations may use other techniques.
The
Java Virtual Machine is required to provided automatic storage
management for objects (class instances and arrays). In the JDK
(1.0.2 and 1.1), for instance, storage space for all objects is
allocated from a central Java heap, and a running Java program
uses pointers into the heap area, called object references, to
access its objects. When the program no longer holds a reference
to an object, that object's space on the heap can be reclaimed, or
garbage collected. Automatic garbage collection means
that the run-time system, not the programmer, is responsible for
tracking memory use and deciding when to free memory that is no
longer needed. This strongly boosts programmer productivity and
code robustness.
A Java Virtual Machine
implementation must include some form of garbage collection, but
the implementation can choose what kind. The JDK (1.0.2 and 1.1)
implements garbage collection with a partially conservative
mark-and-sweep algorithm.
A round of mark-and-sweep garbage
collection starts by identifying any element in a running program
(that is, any element on one of the the program's active thread
execution stacks) that might refer to an object. For pure Java
code, there is no question about precisely which elements are
object references, but Java programs can also include non-Java
native methods, which might have their own object pointers. The
garbage collector is conservative in that it errs on the
side of safety: if an element even looks like it might point to an
object in the central heap, that element gets treated as an object
reference. The garbage collector traces all potential references
to corresponding objects on the heap, and from any references held
by those objects to yet further objects, and so on. It marks
all objects encountered in this transitive process; these objects
are considered the program's set of live objects that must be
maintained. Finally, the garbage collector scans (sweeps)
the heap for all unmarked objects and reassigns their space back
to the pool of free memory available for new allocation. Note that
this method correctly picks up dead reference cycles (e.g., X
refers to Y, which refers to Z, which refers to X, but the program
no longer refers to any of the three), which escape detection by
some other algorithms, such as simple reference counting.
|
|
Q . why does my finalize method never seem to get invoked?
|
Ans
:
Your object's finalize
method will get invoked, but the Java system makes no guarantees
about when; it requires only that an object be finalized before it
is garbage collected.
Objects use
system resources, and a well-behaved object should free its
resources for reuse once the object is no longer needed. One
system resource, storage space for objects, is managed
automatically by the Java Virtual Machine: the virtual machine
detects when an object is no longer usable and eventually reclaims
the object's space for use by new objects. If your object uses any
other system resource, such as a graphics context, an open file,
or a network connection, it is up to you to explicitly manage that
resource. Java's system of finalization provides a simple
notification service with which you can define how your objects
free up their resources.
The core of Java's finalization
system is the finalize method:
protected void finalize() throws Throwable { ... }
The Object
class defines an empty version of this method, which you override
in a subclass when you need to specify finalization actions for
that class. The following code fragment illustrates a class that
provides a method to explicitly clean up system resources, but
also defines finalize as a backup measure:
class MyClass {
AudioDevice device = AudioDevice.acquire();
void cleanUp() {
// ...
if (device != null) {
device.release();
device = null;
}
}
protected void finalize() throws Throwable {
if (device != null)
device.release();
}
super.finalize();
}
// ...
}
The most
common confusion about finalization is when it occurs. There is
much leeway—a virtual machine is required only to obey the
following sequence:
- Your program removes its last
reference to an object.
- The virtual machine invokes
finalize
on that object.
- The virtual machine garbage collects
the object.
What this
means in practice, such as on the JDK 1.0.2 and 1.1 virtual
machines, is that finalization waits for garbage collection, and
then runs immediately prior to the actual reclamation of space.
You can wait indefinitely for finalization to occur, because
garbage collection usually waits until either the system runs low
on memory or there is sufficient slack time in the application. If
you need to, you can initiate finalization and garbage collection
explicitly by invoking System.runFinalization() or System.gc(),
but these methods provide no guarantees either. Finalization or
garbage collection (depending on which method you invoke) will
occur, but there is no guarantee that some specific object of
yours is ready to be finished off.
Because the timing of finalization
is implementation dependent, and even circumstance dependent, you
can't rely on finalization for consistent, timely behavior.
Wherever possible, you should free critical system resources
explicitly in your code and rely on finalization as more of a
backup measure. The AWT code for the Graphics class
(in JDK 1.0.2 and 1.1), for instance, disposes of each system
graphics context explicitly as soon as it can; for safety, the
class also invokes dispose from its finalize method:
/* in Graphics.java (JDK 1.0.2 and 1.1): */
public void finalize() {
dispose();
}
|
|
Q . I'm having trouble invoking methods on the objects returned from Class's forName method-how should I use Class.forName?
|
Ans
:
Invoke newInstance
on the Class object returned by forName
and then cast the resulting instance to a type that the compiler
can access; only then can your program invoke a method on the
instance of the newly loaded class.
The
forName method in class Class is a
hidden gem in the Java system. This method lets your code create a
new class type from a dynamic name—that is, a name given to your
program as a string at run time. What's particularly powerful
about using forName is that neither you nor the
compiler needs to know the exact details of the new class, only
what superclass it extends or interfaces it implements. The forName
method is what enables a Java-capable browser, for instance, to
load arbitrary applets, even though the browser can't know in
advance what Applet subclasses it will be called on
to create.
The forName method
takes a String argument as the name of a class, and
attempts to create a binary representation for a class with that
name. (This process is called class loading.) If it succeeds, it
returns an instance of class Class that represents
the newly created class type. The following code illustrates:
Class newClass = null; // initialize local variable, in case
// forName throws exception
String packageName = "myPackage";
String className = "myClass";
try {
newClass = Class.forName(packageName + "." + className);
} catch (ClassNotFoundException e) { /* ... */ }
The
Class instance returned by forName only
represents the new class type; it is not an instance of that
class. To obtain an instance of the new class, invoke newInstance
on the Class object, which returns an object
reference of type Object. (Note that newInstance
requires that the target class have an accessible no-parameter
constructor.) To access more than plain Object
functionality, you need to cast the object reference to a more
specific type—but what type?
Although the forName
method can create a new class type at run time, the way your code
uses that class is still checked by the compiler, before your
program has a chance to run. This restricts your code to using the
new class and its instances only in terms of a superclass or
interfaces that the compiler can check. For example, suppose that myPackage.myClass
in the previous code fragment implements the Runnable
interface, which contains the single method run. You
can then cast the new instance to Runnable, after
which the compiler will allow you to invoke run on
it:
Object foo = null;
try {
foo = (Runnable) newClass.newInstance();
foo.run(); // invoke method on new instance
} catch (IllegalAccessException e) { // ...
} catch (InstantiationException e) { // ...
}
Thus, the newly
loaded class may have all kinds of wonderful functionality, but
the compiler doesn't know this at compile time; the compiler lets
your code access only those methods (and fields) belonging to an
interface or superclass that it can check.
|
|
Q . Why do I get verifier errors when loading a class file produced by javac?
|
Ans
:
Chances are
the class was compiled with optimization, which can cause the
class file to fail verification; this is an unfortunate flaw in
existing implementations.
The
Java Virtual Machine performs three general processes when it
first attempts to execute code belonging to a class:
- loading:
given the class name, determine the binary form for the class
- linking:
convert the class's binary form so that it can be merged into
the currently executing virtual machine
- initialization:
execute the static initializers for the class and the class
variable initializers
All three processes
are managed by a class loader (an instance of a ClassLoader
subclass). A Java program can have more than one class loader, to
define different policies for finding classes and constraining
their behavior.
The process of linking usually
starts with verification—checking that the class's binary
representation is well formed and the virtual machine instructions
in it obey the constraints of the Java language and Virtual
Machine. Verification adds both security and robustness—it
protects a virtual machine from malicious executable code, and it
weeds out class files that may have been accidentally corrupted in
transmission. An error detected during verification is thrown as
an instance of the VerifyError class.
The javac compiler is
meant to generate only valid executable code, and should always do
so when run in normal mode. However, when you compile with
optimization (javac -O in the JDK 1.0.2 and 1.1), you
run the risk of obtaining code that a verifier will not understand
and will therefore reject.
Java implementations have the leeway
of omitting verification for trusted, local class files, and the
JDK (1.0.2 and 1.1) takes advantage of this. The AppletViewer
application, for instance, uses different class loaders for local
classes versus classes loaded over the network. Its network class
loader verifies each class as it comes in, but its local class
loader omits the verification phase in order to save time. If you
have a class file that loads successfully from the local file
system, but fails when loaded over the network, you are quite
likely seeing the difference between two class loaders, only one
of which is using verification.
You can test your own class files
for well-formedness by using the javap tool in the
JDK (1.0.2 and 1.1). When given the -verify option, javap
simply runs the class you specify through its verifier and reports
success or failure:
% javap verify packageFoo.ClassE
Class packageFoo.ClassE succeeds
|
|
Q . How fast are Java programs compared to equivalent C or C++ programs?
|
Ans
:
The
relative speed depends on the type of program and on whether the
virtual machine implementation uses interpretation or just-in-time
compilation—GUI-intensive programs may have little noticeable
difference, but computation-intensive programs run about 10-20
times slower in interpreted Java Virtual Machine instructions than
in compiled native code.
The
execution speed of a Java program depends on how you run it. Most
of the initial Java Virtual Machine implementations run
interpretively—they translate Java Virtual Machine instructions
to native machine code each time immediately prior to executing
the code. Common estimates are that an interpretive virtual
machine implementation runs around 10-20 times slower than
comparable code compiled directly to the native platform.
This slowdown represents the
worst-case behavior for computation-bound programs, where
intensive, uninterrupted computation comprises most or all of the
program. GUI-dominated programs, on the other hand, typically
spend most of their time waiting for user input; in such programs,
the perceived difference in performance is often minor.
If raw execution speed is the
bottleneck, you might consider using a virtual machine
implementation that includes a just-in-time (JIT) compiler, which
are becoming more and more common. A JIT compiler translates Java
Virtual Machine instructions into native machine code at run time
on the local platform. Once the virtual machine implementation has
this translation, it can run the native code at speeds comparable
to other compiled languages.
|
|
|
|