Cover Story / May 1998

How to Soup Up Java

Will Java always be noticeably slower than C++?
It's a matter of debate, but many experts say no.
They're working on nine ways to boost Java's performance.

Tom R. Halfhill; BYTE Lab Testing By Al Gallant

Software tends to get slower over time, not faster. Otherwise, our applications could still be cruising on a 4.77-MHz IBM PC. But Java is bucking the slothware trend, at least for now. Thanks to the brute force of global research and development, Java is experiencing a growth spurt that will bring it very close to the performance of languages such as C++.

Competitive performance is crucial to Java's success. Java tool vendors and developers agree that sluggish execution is their customers' biggest complaint — both the customers who are using Java and those who are still evaluating it. Indeed, poor performance is second only to the problem of inconsistent cross-platform compatibility, another annoyance of Java's youth.

"There's been a lot of buzz in the marketplace about Java performance, and the fact that it's not as fast as C++, which is the benchmark," says Colette Coad, a partner at Ernst & Young who leads its Java development in the U.S. "It's holding some people back from building enterprise-level, server-based Java applications."

Despite those misgivings, Coad adds, Java continues to score major design wins, especially at large organizations whose need for cross-platform solutions overrides other factors. (See "Java Gets Down to Business," October 1997 BYTE.)

It's happening because Java is already fast enough for many purposes. Often, Java applets are merely the front end for a centralized process, such as a database manager, that's running on a server. It's the database's stored procedures, the server, or the network that's the bottleneck, not the applet. However, it's more often the case that Java is simply too slow for applications that could benefit from its advantages: multiplatform compatibility, developer productivity, and run-time safety.

The same barriers stunted the popularity of earlier object-oriented, dynamic languages, such as Smalltalk and Eiffel. Even more successful languages, such as Visual Basic and PowerBuilder, have been limited by their less-than-C performance. But the tremendous hype over Java is generating an unprecedented amount of R&D at companies and universities all over the world.

In general, researchers are exploring nine approaches to higher performance:

• Better Java compilers. These are the compilers that translate Java source code (.java files) into Java bytecode (.class files) for execution on Java virtual machines (JVMs).

• Faster JVMs. The JVM is the software layer in a Web browser or OS that interprets Java bytecode and handles run-time operations such as garbage collection.

• Bytecode optimizers. These tools apply additional optimizations by recompiling the bytecode produced by other Java compilers, yielding faster class files that still consist of standard bytecode.

• Just-in-time (JIT) compilers. When a JVM loads a program's classes, a JIT compiler quickly translates the bytecode into native machine code and then caches it in memory. JIT compilers are common in Web browsers.

• Dynamic or adaptive compilers. These high-tech compilers intelligently translate Java bytecode into native machine code at run time based on profiles of how the program is running. An example is Sun's upcoming HotSpot technology.

• Static native compilers. These tools translate Java source code or bytecode into native object code at design time, just like compilers for C/C++.

• Native method calls. Java applications can invoke native executable files, including DLLs written in C++ and services in the native OS. (See the sidebar "Calling Native Code".)

• Java chips. A new breed of microprocessors can directly execute Java bytecode as their native machine language. Most of these chips are designed for low-cost devices. (See the sidebar "Java Chips: The Hardware Solution.")

• Writing better code. (Last but not least!) Developers can follow good programming practices and exploit Java's idiosyncrasies. (See the sidebar "Speed Tips for Java Coders".)

Why Java Is Slow

To grasp how these techniques work, it's crucial to understand why Java is slow in the first place. One reason is that Java is a dynamically bound object-oriented language. Java programs don't bind their classes until run time; only then can they resolve polymorphic method calls inside their deep hierarchies of inherited objects. When a method in a child class overrides a method in a parent class, a dynamically bound program won't discover which method takes precedence until run time, and the precedence can change if the program loads another class. (A method is the Java counterpart to a function, procedure, or subroutine in other languages.)

C compilers statically bind a program during compilation by inserting pointers to the function calls, so the program does not have to resolve those references at run time. But C++ allows virtual methods that are late-bound and polymorphic like Java methods, and C++ also suffers from the hierarchical nesting of objects. That's why C++ is generally slower than C, a classic procedural language.

To recover some of the efficiency of static binding, Java defines a bytecode instruction called INVOKE_VIRTUAL_QUICK that a smart JVM can substitute at run time for the more common INVOKE_VIRTUAL instruction. The quick version bypasses a few table lookups after the JVM has already resolved a method call. In general, though, dynamic binding and object hierarchies play a relatively minor role in Java's poor performance. A much larger factor is Java's almost unprecedented degree of hardware abstraction.

Higher levels of abstraction have been a trend in software development for half a century. Starting from the hard-wired logic of ENIAC, computer programming has advanced to machine language assemblers, high-level-language compilers, high-level-language interpreters, OS-level APIs, and object-oriented class libraries. Each step up the ladder makes it less necessary for application programmers to bother with hardware details. But each step also consumes more CPU cycles. Fortunately, that penalty is largely hidden by a parallel trend in hardware: Moore's law. The circuit density (and thus the performance) of CPUs roughly doubles every 18 months.

Java carries abstraction to a new height. It's a programming language that's also a platform. The JVM contains a software representation of a CPU, complete with its own instruction set. (Java chips turn that virtual CPU into real hardware.) The bytecodes in Java class files contain instructions for the virtual CPU, and the classes will run on any native platform with a compatible JVM. By writing to the Java APIs and compiling to bytecode, developers don't have to worry about the native CPU or OS.

To make this work, the JVM has an interpreter that translates bytecode instructions into native CPU instructions at run time. Interpreted programs always run slower than natively compiled programs, because a compiler has already turned a native program into a binary executable file at design time. No run-time translation is necessary.

Run-time interpreting alone would put Java at a disadvantage, but there's much more. Java removes another burden from programmers by automatically handling memory management. In C/C++, programmers are responsible for allocating memory, using the memory properly, and releasing the memory when they're done with it. Memory leaks and pointer bugs are the most common reasons why C/C++ programs crash. Java avoids those headaches by using a garbage collector — an automatic background process that frees up memory when a program no longer needs it. But garbage collection adds more overhead to execution.

The JVM eliminates another common hazard of C/C++ by automatically checking for array-bounds exceptions -- attempts to reference array elements that don't exist. The JVM also checks for null pointers, division-by-zero errors, illegal string-to-number conversions, invalid type casting, and a host of other exceptions that threaten to crash a program.

Java's powerful multithreading adds significant overhead. Threading is easy in Java, and programmers can prevent thread conflicts by using a built-in keyword (synchronized) that ensures only one thread at a time is executing a particular method. However, calling a synchronized method takes longer, because the JVM's thread monitor must check to see if any other threads are using the method. This penalty applies even to single-threaded programs that call synchronized methods — and hundreds of methods in Java's standard classes are synchronized.

Don't forget Java's dynamic loading. The JVM can load new classes at any time while a program is running, which means it needs resources to load, verify, and initialize the new class. Most native platforms can dynamically load and link, too — that's how Windows DLLs work — but programmers have to explicitly write a program that way. In Java, dynamic class loading demands no extra effort, because a program can automatically load any class file whenever it's needed. Therefore, dynamic loading happens more often.

Finally, there are Java's restrictive but effective security measures. Web browsers implement a Java security manager that normally stops applets from reading or writing to local disk drives, calling native executable files, connecting to servers other than the host, and doing some other potentially dangerous things. Checking for those violations soaks up more CPU cycles. But Java applications don't suffer as much in this regard. Unlike applets, they're stand-alone programs that can do virtually anything a native program can do.

It's a trade-off. In return for multiplatform compatibility, greater safety, the flexibility of dynamic loading, and higher programmer productivity, users pay the price of slower execution. Of course, like all software, Java will ride the coattails of Moore's law. But even if CPUs never get any faster, Java's performance would still get better.

The Garbageman

To begin with, JVMs are getting faster. JVMs based on Sun's Java Development Kit (JDK) 1.1 are about twice as fast as those based on JDK 1.0.2. One reason is that Sun streamlined the event model.

The old model would "broadcast" events (e.g., mouse-clicks, mouse movements, and keystrokes) throughout an entire program until an object trapped the event with event-handling code. That was very inefficient, especially for high-volume events such as mouse movements. Under the new event model, objects can register with another object called a listener to hear only those events in which they're interested. For example, a button might want to listen only for mouse-clicks. From the button's point of view, all other events are like trees falling in an uninhabited forest: They don't make a sound.

Changing the event model might seem like a small thing, but it makes a big difference at run time. JVMs spend roughly 50 percent of their time interpreting bytecode (assuming there's no JIT compiler), so anything that speeds up that process yields big gains in performance.

JVMs may spend another 15 percent to 25 percent of their time on garbage collection. Why so much? Because object-oriented languages such as Java make heavy use of memory.

Classes are templates that define objects. A program can create any number of objects from a class, and each instance of an object stays in a memory heap until the program no longer needs it. The garbage collector in Sun's current JVM uses a simple mark-and-sweep algorithm that periodically marks all unreferenced objects and sweeps them away in a single pass.

If you chart the memory usage of a JVM with this kind of garbage collector, you'll see a jagged sawtooth pattern (see the screen). The memory consumption goes way up, then way down, then way up again. The JVM could smooth out those jaggies by scavenging the heap more often, but it would steal CPU cycles away from other processes, including the program's execution threads.

Luckily, JVM vendors can draw on two decades of research into garbage collectors, thanks to pioneers who faced the same problem with Lisp, Smalltalk, and other languages. Later this year, the JVM that includes Sun's HotSpot technology will introduce a new collector based on an advanced generational algorithm.

Generational collectors are more efficient because they sweep smaller parts of the heap more often. They're called generational because they base their sweeps on the "age" of objects. "Most objects die young," explains Tim Lindholm, a senior staff engineer at JavaSoft. In other words, a program tends to use an object briefly before discarding it. Sun's new JVM keeps the references to newly created objects in a special part of the heap called the "nursery." The garbage collector sweeps the nursery often to make room for more infant objects.

Objects that outlive a few sweeps in the nursery move on to another part of the heap reserved for middle-aged objects. The garbage collector doesn't visit them as often. Longer-lived objects eventually graduate to a third area of the heap that the collector sweeps the least often of all. And to optimize the technique still further, Sun uses different collection algorithms for each region of the heap. For example, the collector sweeps the oldest region with a "train" algorithm that divides memory into chunks called "cars." If the collector is pressed for time, it can sweep individual cars instead of the whole train.

The garbage collector in Microsoft's JVM uses similar techniques, according to Joe Herman, Microsoft product manager for Internet platforms. It keeps a dynamically ordered table of objects and collects only part of the list at a time. As with Sun's new garbage collector, it tends to level out the jagged peaks and valleys of memory consumption. Because most Java vendors license their JVMs from either Sun or Microsoft, these advances will ripple through the entire Java community.

Thread synchronization is another run-time task that's ripe for improvement. Like garbage collection, it might account for 15 percent to 25 percent of a JVM's work load. Symantec licensed Sun's JVM and extensively modified the thread monitor — and, according to Al Bannon, Symantec's director of developer relations, thread synchronization is now 80 percent to 150 percent faster. Symantec licenses those modifications to other companies.

Sun improved the performance of its own JVM for Solaris by rewriting it to map individual Java threads onto native OS threads. The earlier version piggybacked all Java threads on a single Solaris thread, which meant the OS couldn't dispatch Java threads to different CPUs on multiprocessor systems. Microsoft's JVM for Windows NT also maps Java threads directly to native threads. But it isn't feasible with an OS such as Windows 3.1, which has little or no native support for multithreading.

This is one area where application programmers can make a difference, too. Sun's HotJava browser used to require the JVM to spend 25 percent of its time monitoring thread synchronization in the program, says Peter Kessler, a senior staff engineer at JavaSoft. By rewriting the browser to optimize threading, Kessler says Sun reduced that work load to 10 percent to 15 percent.

Better Compilers

Despite the ongoing improvements in JVMs, that's not where Java will realize the greatest gains in performance. Interpreted execution simply isn't fast enough for most purposes. That's why computer science graduates who are pondering their uncertain futures should consider compilers.

There are five types of compilers relevant to Java: source code compilers, bytecode optimizers, JIT compilers, dynamic/adaptive compilers, and static native compilers. It's possible for a single Java program to undergo as many as three different levels of compilation with these tools. And because today's Java compilers are relatively primitive, the potential gains are enormous.

Source code compilers turn Java source files into class files of bytecode, ready to run on any compatible JVM. By modern standards, Java source compilers don't do much optimization. The JAVAC compiler in Sun's JDK — the benchmark Java source compiler — has only one command-line optimization switch (-O), and it applies only a few simple optimizations, such as method inlining. (See "Better Java Programming," September 1996 BYTE.)

Other Java tool vendors have their own source compilers. Most vendors concentrate on making their compilers work faster, rather than making the compilers produce faster code. Fast compilers shorten the edit-compile-test cycle for programmers but do nothing for users. Tool vendors are just starting to add the optimizations found in mature compilers for other languages.

There's nothing especially new about those optimizations — most of them are straight out of computer science textbooks. It's just a matter of bringing Java compilers up to date. For those who can't wait, one alternative is a bytecode optimizer. This tool recompiles bytecode (you don't need the source code) into optimized bytecode that's still platform-independent. Some examples are DashO and DashO Pro, from Preemptive Solutions.

DashO is the brainchild of Paul Tyma, president and chief scientist of Preemptive, who's writing his doctoral dissertation on Java performance at the University of Syracuse. Tyma says DashO can speed up typical logic code by about 30 percent, with loops and matrix multiplication showing even greater improvement. DashO applies several classic compiler optimizations to Java bytecode, and Tyma says future versions will introduce some of his own Java-specific optimizations. Here are some examples:

• Transient variable caching. This technique (for which a patent is pending) recognizes that Java's virtual CPU has a stack instead of registers. When a program swaps the values of two variables, an ordinary Java compiler generates three pairs of stack load and store instructions. DashO uses the stack in a smarter way to generate only two pairs of stack loads and stores.

• Loop unrolling. This is a classic optimization that replaces short loops with a series of instructions that duplicate the function of the loop. It's faster because it eliminates a compare instruction and a branch instruction for every iteration through the loop.

• Loop-invariant code motion. Some loops contain statements that execute during every iteration but always yield the same results. (Example: a+1=b, where neither variable is a loop variable.) Good compilers move this code outside the loop so it executes only once.

• Common subexpression elimination. Given a statement such as x=(y+5)+(y+5), a smart compiler will reuse the result of the first expression rather than evaluate the second expression.

• Tail-recursion elimination. In some recursive algorithms, only the tail portion is recursive. Often, it's more efficient for the compiler to transform the recursive algorithm into an iterative algorithm.

• Statically analyzed polymorphic inlining. This lets an optimizer inline public methods. (Java compilers can inline private, final, and static methods, but they can't inline a public method, because a polymorphic method might override it.) First, the optimizer analyzes the bytecode to find public methods that aren't overridden. Then it inlines those methods, eliminating the JVM's overhead of checking for child methods. Drawback: There will be a problem if the program dynamically loads a class that tries to override the inlined public methods.

• Method desynchronization. Tyma says a future version of DashO will use this Java-specific technique to fix the thread- synchronization problem described earlier. DashO will analyze the bytecode to see if a synchronized method really needs to be synchronized; if not, it removes the lock. For example, if method A is the only method that calls method B, and if method A is synchronized, there's no need to synchronize B. Removing the lock on B reduces the overhead of calling the method without compromising the thread safety of the program.

It's possible that these optimizations and more will eventually find their way into Java source compilers, reducing the need for a recompiler such as DashO. But tool vendors seem to be focusing on three other types of compilers: JIT compilers, dynamic/adaptive compilers, and static native compilers.

Going Native

At some point, bytecode must become native machine code so it can execute on a real CPU (unless the real CPU is a Java chip). The slowest way is to let the JVM's interpreter translate the bytecodes one by one, over and over again. A much faster way is to bypass the interpreter by compiling the bytecode into machine code and then to cache the machine code in memory. The main difference between JIT compilers, adaptive compilers, and static native compilers is when they do that translation. The first two types do it dynamically, at run time; static compilers do it at design time.

There's a fine line, or perhaps no line, between JIT compilers and adaptive compilers. Both are really dynamic compilers. The simplest JIT compilers translate bytecode into machine code immediately after the JVM loads each class into memory. Then the JIT compiler steps out of the way and lets the program run. If the JVM dynamically loads another class at a later time, the JIT compiler may compile the new class as well.

However, a JIT compiler may decide not to compile every class, especially if a class is so large that compiling it would seriously delay execution. That's the weakness of a JIT compiler — it has to be quick and dirty. "A lot of optimizations take a lot of time," notes Tyma."JITs can't take too much time or the user will say, 'Whoa, when is this program going to start?'"

The solution: smarter JIT compilers. Borland's JIT compiler can compile individual methods without compiling a whole class. It doesn't bother to compile static initializers (which a program calls only once per class), and it won't compile an object constructor method unless the program calls it more than once.

The JIT compilers from Microsoft and Symantec are similarly intelligent, and they're making rapid progress. Symantec's first JIT compiler appeared in March 1996; six months later, version 2.0 was 50 percent faster, according to several benchmarks. Symantec says version 3.0, introduced last December, beats that by another 50 percent. Depending on the nature of the code, today's JIT compilers can run a program from five times to 20 times faster than an interpreter. (See the sidebar "Benchmarking Java".)

Java programs that are compiled with Microsoft's JIT compiler can achieve 30 percent to 40 percent of the performance of native C++ programs, says Bill Dunlap, product manager of Visual J++. He estimates that JIT-compiled Java could eventually attain 60 percent to 70 percent of the speed of native C++. Symantec is more optimistic, predicting that fast JVMs and JIT compilers could eventually match C++.

To reach those goals, JIT compilers are doing some amazing things. Borland's JIT compiler doesn't stand on the sidelines after compiling part of a class — sometimes it compiles additional methods later. Microsoft's JIT compiler performs a quick data flow analysis during the initial compile and then continues to analyze and compile parts of the program during execution (except for small applets, which aren't worth the trouble).

"I think we've only seen the tip of the iceberg for JVMs and JITs," says John Robbins, engineering product manager for performance tools at NuMega."There are a lot of smart companies working on this, and I think the real breakthrough is going to happen two or three years from now."

The Great Hype Hope?

The best JIT compilers are really adaptive compilers. Adaptive compilers create a profile of a program before and during execution, identify the performance bottlenecks, and then compile or recompile parts of the program to relieve the bottlenecks. This is the cornerstone of Sun's HotSpot technology, which will probably appear later this year in JDK 1.2.

HotSpot (a code name) isn't new technology. It grew from research that began at Stanford University in 1987, based on earlier work on Smalltalk at Xerox Palo Alto Research Center (PARC). Stanford researchers created an object-oriented language called Self to explore dynamic compilation. Several of them later founded a small company known as LongView Technologies (or Animorphic Systems). Sun acquired the company to make HotSpot.

Sun is using HotSpot as an umbrella term for several acceleration techniques, including the generational garbage collector described earlier and a new thread monitor. But the most interesting part is adaptive compilation.

HotSpot is like a hyperactive JIT compiler — it profiles, it compiles, it recompiles. It can start compiling when the program launches or wait to see how the program runs. It can make decisions about which parts of a program to compile according to how often the code executes or how much time the code takes to execute. It balances the time required for compilation against the time saved by faster execution.

It can apply additional optimizations by recompiling code that's already been compiled. It can flush compiled code from memory to make room for newly compiled code. It can inline the parameters of method calls if the program always calls a method with the same parameters. It can even turn bytecode into buttered popcorn. (OK, that last claim was a joke.)

Or maybe it's not a joke. Critics accuse Sun of overselling HotSpot. Last year, Sun speculated that HotSpot could boost Java even beyond the performance of C++. Lately, Sun has been more conservative. "We think it's going to be a horse race," says JavaSoft's Peter Kessler. "But it would be irresponsible to say at this point that a Java program will be faster than the same program written in C++."

One of the foremost authorities on dynamic compilers is Dr. Craig Chambers, an associate professor in the University of Washington's department of computer science and engineering. Chambers worked on the Self project at Stanford in the 1980s and continues to develop experimental languages (such as Cecil) and adaptive compilers (such as Vortex)."There will be Java systems that match the performance of C++," he says. But he adds that some programs — perhaps 5 percent — will always be faster in C++, because of Java's run-time overhead and dynamic nature.

Others, however, denounce HotSpot as an overhyped JIT compiler. "Dynamic processes like garbage collection are one of the things that will keep Java from ever achieving the performance of native compiled languages like C++," declares Microsoft's Herman.

Dynamic vs. Static

Static compilers would seem to enjoy an enormous advantage over dynamic compilers: virtually unlimited time to analyze and optimize a program. Programmers are accustomed to lengthy builds, while users are not. Besides that, static compilers usually work from source code, not bytecode, so it should be easier for them to deduce the semantics of a program before optimizing it. And static compilers can globally analyze a whole program, so they can apply more aggressive optimizations. Dynamic compilers are limited to peephole (local code) optimizations.

But dynamic compilers have a big advantage, too: They profile a program while it's running, so they don't have to guess where the actual bottlenecks are. They know which way branches are forking, how many objects the program is creating, and the actual size of arrays. Indeed, they can adapt to the different ways that different people use the same program. Working from bytecode instead of source code isn't a huge problem, because bytecode isn't nearly as obscure as native object code. In fact, bytecode is similar to the intermediate code generated by the front ends of some compilers, says Jim Russell, manager of Java technology for IBM Research.

Static native compilers for Java are available from SuperCede, Symantec, Cosmo Software (a Silicon Graphics spin-off), IBM, Tower Technology, and the Open Group, with more on the way from Microsoft, Borland, Instantiations, and others. They target platforms as varied as Windows (x86), IBM OS/2, AS/400, AIX, Mips Rx000, HP-UX, and Linux (x86).

Most of these compilers work from bytecode, not source code, so developers can compile third-party JavaBeans and class libraries that don't include source code. The compilers produce native-code executable files — which are, of course, limited to a single platform.

That doesn't necessarily violate the write-once, run-anywhere religion. A compiled Java program still exists in its original cross-platform source code and bytecode, unless the programmers lose their disks. More important, static compilation makes sense when developers are deploying a Java application on a known platform, especially a server. This wouldn't make sense for an applet, which downloads at run time to an unknown client. Dynamic compilers are better for applets.

Just because a Java program is statically compiled doesn't mean it escapes Java's run-time overhead. The program still needs a run-time system to handle garbage collection, thread synchronization, bounds checking, exception handling, and — ideally — dynamic class loading. That's why a statically compiled Java program often isn't faster than the same program running on a good JVM with a smart JIT compiler.

That may change as the compilers get better. Most of them don't do much optimizing, and some are rather crude. One of them converts Java bytecode into C source code, which then feeds through a regular C compiler to produce native object code. Another compiler chokes on finalize() methods, and few of them can handle dynamic class loading.

Several sources told BYTE that static compilers for Java could eventually outperform the best JIT compilers and deliver near-native performance. Those sources include Tim Freehill, the engineering manager for Metrowerks' CodeWarrior; Robert "Rock" Howard, chief technology officer at Tower Technology; Jayson Minard, the product architect for Borland's JBuilder; Jim Russell, manager of Java technology and applications at IBM Research; and Allen Wirfs-Brock, chief technology officer at Instantiations.

On the other hand, Sun, Sybase, Symantec, and Microsoft are leaning toward dynamic compilers or doubt that any technology can make Java as fast as C++. But that isn't necessarily stopping them from developing static compilers. "The move is toward native compilation," says Microsoft's Herman. "We have to get to the point where we have performance comparable to native code, and we're going to need native compilers to get there."

Fast Enough

Does Java really need to be as fast as C++ to succeed? History says no.

Keep in mind the 50-year trend toward higher levels of hardware abstraction. Although Java needs competitive performance to succeed, it doesn't need superlative performance. When there's a trade-off between raw performance on one side and code productivity and portability on the other, developers rarely choose performance.

The C language replaced assembly language for the vast majority of development, because it's an easier, more portable language than assembly language. C++ has been replacing C for the same reasons. Likewise, Java will replace C++. Sure, some programmers still resort to C or assembly language when they need to write low-level code, device drivers, or critical loops. Java programmers can do that, too.

Besides high productivity and portability, Java offers two additional advantages that help make up for its slow performance: run-time safety and code longevity. Both will become more important in the future.

The PC industry gets away with buggy code on client systems today, but customers won't tolerate it on their mission-critical servers, and they're growing more aware of what it's costing them on clients. That's why mainframes are still popular: They really work. To build reliable systems, PC developers must trade the guns and knives of C++ for the seat belts and air bags of a modern language.

Meanwhile, users are learning the hard way that mission-critical code often lives for decades. Consider the year 2000 problem or the U.S. government's troubles with the air-traffic-control system.

Millions of lines of code written today will still be running 30 years from now. Nobody can predict what platforms will be popular at that time. A virtual platform such as Java is cross-platform not only in the horizontal dimension, but also in the temporal dimension. No matter what new platforms appear, only the JVM and native compilers will have to be ported — not the applications.

All those factors will outweigh Java's laggard performance, as long as Java can achieve at least 50 percent of C++ native performance. That's a much smaller gap than the difference between C++ and assembly language. Even Microsoft thinks Java can reach 60 percent to 70 percent of native performance, and most experts are more optimistic. The enormous amount of effort invested toward that goal and the numerous ways of getting there virtually guarantee that if Java fails, it won't be because it isn't fast enough.

Sidebars:

Where to Find

Borland International
Scotts Valley, CA
408-431-1000
http://www.borland.com/jbuilder/

Instantiations
Tualatin, OR
503-612-9337
http://www.instantiations.com/

Intel (VTune)
Santa Clara, CA
408-765-8080
http://147.229.124.84/Intel/support.intel.com/design/perftool/vtune/index-1.htm

KL Group
Toronto, Ontario, Canada
416-594-1026
http://www.klg.com/

Metrowerks
Austin, TX
512-873-4700
http://www.metrowerks.com/

Microsoft
Redmond, WA
425-882-8080
http://www.microsoft.com/java/

NuMega
Nashua, NH
603-578-8400
http://www.numega.com/

Patriot Scientific
San Diego, CA
619-674-5000
http://www.ptsc.com/

Preemptive Solutions
Cleveland, OH
216-732-5895
http://www.preemptive.com/

Sun Microelectronics
Palo Alto, CA
650-960-1300
http://www.sun.com/developers/javachips.html

Sun Microsystems/JavaSoft
Palo Alto, CA
650-786-7737
http://www.sun.com/java/

SuperCede
Bellevue, WA
425-462-7242
http://www.supercede.com

Symantec
Cupertino, CA
408-253-9600
http://www.symantec.com/domain/cafe/deved/../bytelink.html

Tower Technology
Austin, TX
512-452-9455
http://www.twr.com/

A Java Glossary

Bytecode: Java's object code or machine language. A compiler turns
Java source code into bytecode and stores it in a class file. Java
chips can execute bytecode directly, but all other CPUs require an
interpreter or a compiler to translate the bytecode into the CPU's
native object code.

Inlining: Copying the body of a method into the body of another
method. Example: If method A calls method B, a programmer or compiler
can copy the code of method B into method A. This makes it
unnecessary for A to call B at run time, thereby speeding up
execution — at the expense of code size.

Polymorphism: The ability of methods in object-oriented programs to
override methods of the same name in other classes. If a child class
has a method whose name is identical to a method in a parent class,
the child method takes precedence.

Thread: A process within a program. A multithreaded program can
execute two or more threads by distributing time slices from the CPU,
so the program appears to be executing multiple operations
simultaneously.

9 Ways to a Faster Java

Nine ways to faster
                    Java code.

Java's Run-Time Overhead

Java's run-time
                    overhead.
JVMs spend only about half of their time executing program instructions.

Cut Through the Garbage

Graph showing Java
                    garbage collection.
This sawtooth pattern is typical of memory usage
when Java's garbage collector disposes of objects en masse.


Tom R. Halfhill is a BYTE senior editor based in San Mateo, California. You can reach him at tom.halfhill@byte.com.

Copyright © 1994-1998 BYTE

Return to Tom's BYTE index page