epubjs
Version:
Render ePub documents in the browser, across many devices
96 lines (95 loc) • 9.4 kB
HTML
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>A Virtual Machine</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="A Virtual Machine"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-1-SECT-2"/>A Virtual Machine</h1></div></div></div><p><a id="I_indexterm1_id632746" class="indexterm"/> <a id="I_indexterm1_id632753" class="indexterm"/> <a id="idx10049" class="indexterm"/>Java is both a compiled and an interpreted language. Java
source code is turned into simple binary instructions, much like ordinary
microprocessor machine code. However, whereas C or C++ source is reduced
to native instructions for a particular model of processor, Java source is
compiled into a universal format—instructions for a <span class="emphasis"><em>virtual
machine</em></span>.</p><p>Compiled Java <a id="I_indexterm1_id632782" class="indexterm"/><span class="emphasis"><em>bytecode</em></span> is executed by a Java runtime
interpreter. The runtime system performs all the normal activities of a
hardware processor, but it does so in a safe, virtual environment. It
executes a stack-based instruction set and manages memory like an
operating system. It creates and manipulates primitive data types and
loads and invokes newly referenced blocks of code. Most importantly, it
does all this in accordance with a strictly defined open specification
that can be implemented by anyone who wants to produce a Java-compliant
virtual machine. Together, the virtual machine and language definition
provide a complete specification. There are no features of the base Java
language left undefined or
implementation-dependent. For example, Java specifies the sizes and
mathematical properties of all its primitive data types rather than
leaving it up to the platform implementation.<a id="I_indexterm1_id632805" class="indexterm"/></p><p><a id="I_indexterm1_id632813" class="indexterm"/> <a id="I_indexterm1_id632820" class="indexterm"/>The Java interpreter is relatively lightweight and small; it
can be implemented in whatever form is desirable for a particular
platform. The interpreter may be run as a separate application or it can
be embedded in another piece of software, such as a web browser. Put
together, this means that Java code is implicitly portable. The same Java
application bytecode can run on any platform that provides a Java runtime
environment, as shown in <a class="xref" href="ch01s02.html#learnjava3-CHP-1-FIG-1" title="Figure 1-1. The Java runtime environment">Figure 1-1</a>. You
don’t have to produce alternative versions of your application for
different platforms, and you don’t have to distribute source code to end
users.</p><div class="figure"><a id="learnjava3-CHP-1-FIG-1"/><div class="figure-contents"><div class="mediaobject"><a id="I_1_tt4"/><img src="httpatomoreillycomsourceoreillyimages1707596.png" alt="The Java runtime environment"/></div></div><p class="title">Figure 1-1. The Java runtime environment</p></div><p>The fundamental unit of Java code is the <a id="I_indexterm1_id632860" class="indexterm"/><span class="emphasis"><em>class</em></span>. As in other object-oriented
languages, classes are application components that hold executable code
and data. Compiled Java classes are distributed in a universal binary
format that contains Java bytecode and other class information. Classes
can be maintained discretely and stored in files or archives locally or on
a network server. Classes are located and loaded dynamically at runtime as
they are needed by an application.</p><p><a id="I_indexterm1_id632875" class="indexterm"/> <a id="I_indexterm1_id632882" class="indexterm"/> <a id="I_indexterm1_id632891" class="indexterm"/>In addition to the platform-specific runtime system, Java
has a number of fundamental classes that contain architecture-dependent
methods. These <span class="emphasis"><em>native methods</em></span> serve as the gateway
between the Java virtual machine and the real world. They are implemented
in a natively compiled language on the host platform and provide low-level
access to resources such as the network, the windowing system, and the
host filesystem. The vast majority of Java, however, is written in Java
itself—bootstrapped from these basic primitives—and is therefore portable. This
includes fundamental Java tools such as the Java compiler, networking, and
GUI libraries, which are also written in Java and are therefore available
on all Java platforms in exactly the same way without porting.</p><p><a id="I_indexterm1_id632917" class="indexterm"/> <a id="I_indexterm1_id632923" class="indexterm"/> <a id="I_indexterm1_id632930" class="indexterm"/> <a id="I_indexterm1_id632937" class="indexterm"/> <a id="idx10025" class="indexterm"/>Historically, interpreters have been considered slow, but
Java is not a traditional interpreted language. In addition to compiling
source code down to portable bytecode, Java has also been carefully
designed so that software implementations of the runtime system can
further optimize their performance by compiling bytecode to native machine
code on the fly. This is called just-in-time (JIT) or dynamic compilation.
With JIT compilation, Java code can execute as fast as native code and
maintain its transportability and security.</p><p>This is an often misunderstood point among those who want to compare
language performance. There is only one intrinsic performance penalty that
compiled Java code suffers at runtime for the sake of security and virtual
machine design—array bounds checking. Everything else can be optimized to
native code just as it can with a statically compiled language. Going
beyond that, the Java language includes more structural information than
many other languages, providing for more types of optimizations. Also
remember that these optimizations can be made at runtime, taking into
account the actual application behavior and characteristics. What can be
done at compile time that can’t be done better at runtime? Well, there is
a tradeoff: time.</p><p><a id="I_indexterm1_id632975" class="indexterm"/> <a id="I_indexterm1_id632981" class="indexterm"/> The problem with a traditional JIT compilation is that
optimizing code takes time. So a JIT compiler can produce decent results,
but may suffer a significant latency when the application starts up. This
is generally not a problem for long-running server-side applications, but
is a serious problem for client-side software and applications that run on
smaller devices with limited capabilities. To address this, Java’s
compiler technology, called HotSpot, uses a trick called
<span class="emphasis"><em>adaptive compilation</em></span>. If you look at what programs
actually spend their time doing, it turns out that they spend almost all
their time executing a relatively small part of the code again and again.
The chunk of code that is executed repeatedly may be only a small fraction
of the total program, but its behavior determines the program’s overall
performance. Adaptive compilation also allows the Java runtime to take
advantage of new kinds of optimizations that simply can’t be done in a
statically compiled language, hence the claim that Java code can run
faster than C/C++ in some cases.</p><p><a id="I_indexterm1_id633011" class="indexterm"/> <a id="I_indexterm1_id633018" class="indexterm"/>To take advantage of this fact, HotSpot starts out as a
normal Java bytecode interpreter, but with a difference: it measures
(profiles) the code as it is executing to see what parts are being
executed repeatedly. Once it knows which parts of the code are crucial to
performance, HotSpot compiles those sections into optimal native machine
code. Since it compiles only a small portion of the program into machine
code, it can afford to take the time necessary to optimize those portions.
The rest of the program may not need to be compiled at all—just
interpreted—saving memory and time. In fact, the Java VM can run in one of
two modes: client and server, which determine whether it emphasizes quick
startup time and memory conservation or flat out performance.</p><p>A natural question to ask at this point is, Why throw away all this
good profiling information each time an application shuts down? Well, Sun
partially broached this topic with the release of Java 5.0 through the use
of shared, read-only classes that are stored persistently in an optimized
form. This significantly reduced both the startup time and overhead of
running many Java applications on a given machine. The technology for
doing this is complex, but the idea is simple: optimize the parts of the
program that need to go fast and don’t worry about the rest.<a id="I_indexterm1_id633050" class="indexterm"/></p></div></body></html>