The evolution of Java technology for Internet appliances and embedded devices

TechOnline India - August 18, 2010

Some Internet appliance and embedded device developers have concluded that current implementations of the Java specifications are either functionally incomplete for their use, too slow, too unreliable or too big. This paper discusses alternative ways to implement Java technology to effectively address these legitimate concerns.

Some Internet appliance and embedded device developers have concluded that current implementations of the Java specifications are either functionally incomplete for their use, too slow, too unreliable or too big. This paper discusses alternative ways to implement Java technology to effectively address these legitimate concerns.

Sun Microsystems estimates there are more than 2.5 million developers using Java technology today. While there are many documented success stories regarding the use of Java platforms for corporate or enterprise applications, there are few public illustrations of the use of Java technology for Internet appliance and embedded device development. In order to look at the current situation more objectively, we will review "Java technology" from two perspectives: 1) specification, and 2) implementation.

As a longtime developer and provider of virtual machine technologies, Insignia Solutions believes that the current specifications for Java platforms (including Enterprise Java, PersonalJava, and EmbeddedJava) are not at fault, but rather, most current implementations, particularly for PersonalJava and EmbeddedJava, are not viable for most Internet appliance and embedded devices. (Other specifications for application specific platforms called "profiles" (e.g., Java TV and JavaPhone) have not reached industry-wide endorsement, so it's too early to comment on their suitability.)

The paper begins with an introduction to Java technology that will orient readers who are new to the subject, and possibly serve as a refresher for those already acquainted with it.

Java technology designed for the Internet

Sun Microsystems introduced the Java platform in 1995 as a new programming language and runtime environment ideally suited for Internet-related applications. Building on its long-standing corporate theme, "The Network Is The Computer," Sun recognized that more powerful microprocessors were beginning to be used in many consumer and embedded devices, which were, increasingly, connected to networks.

Network connectivity began with workstations and personal computers, quickly followed by printers, scanners, copiers, and various other types of office equipment. More recently, newer devices - such as personal digital assistants, set-top boxes, two-way pagers, smartphones, and even digital watches - have been enhanced with microprocessors and connected to networks.

Prior to Sun's introduction of the Java platform, the network was viewed primarily as a vast system for storing and serving up relatively static information, and the phrase "The Network Is the Virtual Disk Drive" was probably more fitting. Today, Java technology promises to extend the usefulness of networks by providing an efficient means for storing and distributing dynamic and extensible functionality to networked computing devices.

The Java architecture is comprised of several distinct but interrelated technologies, each of which is defined in specifications from Sun Microsystems. These technologies include the Java programming language, the Java virtual machine, and the Java API. We will briefly discuss the Java programming language and the Java API before exploring the detailed implementation alternatives for the Java virtual machine.

Together, the Java programming language and APIs define the "interfaces" between the developer and the Java virtual machine. Each Java virtual machine implementation must be able to execute Java programs, but the way in which this occurs is left to the discretion of the developer of the Java virtual machine itself.

{pagebreak}Overview of the Java programming language

The Java programming language inherits much of its syntactical rules, and therefore familiarity, from C/C++. In order to make Java technology more robust, however, some fundamental changes were made to the language definition. For example, the Java language does not permit direct access to memory nor is pointer arithmetic part of the language. To directly access memory, for example, developers may write a "native method" in C and call it from the Java application via the Java Native Interface (JNI).

The Java language supports a true implementation of an array object. Unlike C/C++, the Java runtime performs array bounds to insure the application does not attempt to access any element beyond the end of the defined array. The Java language also prevents implicit typecasting to objects of dissimilar types, especially when precision might be lost (e.g., float typecast to an int). Although these features may be viewed by some as restrictions in the Java language, there is emerging proof from actual development projects that Java technology can reduce overall development time and improve the reliability of the application - a key concern for Internet appliance and embedded device developers.

Viewed at a higher level, Java technology is a pure object-oriented language. For example, the "Hello World" program can only be implemented as a class object. Projects implemented with Java technology should realize the benefits of code reuse and a higher degree of maintainability over time.

The key element of the Java language that is highly relevant to networks is its size as an executable. Since Java applications are intended to be distributed easily across networks, it is important to maintain a compact representation of the code. Java source files entered by the developer are compiled into the efficient and portable bytecode instruction set, which is the representation executed by the Java virtual machine. Rather than requiring an independent set of run-time libraries, like C/C++, multi-threading is inherent to the Java language.

The Java API can be described at either the individual detailed level or as a higher-level collection of functionality. The hierarchy of definitions works something like this: at the highest level, everything can be referred to simply as "Java technology." Below that, Sun has defined some natural sub-setting of Java technology, for example, described previously as specifications for Enterprise Java, PersonalJava, and EmbeddedJava application environments. These specifications are intended to define the various functional elements and class libraries that are most likely to address the requirements of certain categories of applications.

The objective is to help ensure that a Java application written for one category of computing device (e.g., smartphone) will be able to run on another computing device in the same category. Each specification for a Java application environment generally requires a functionally equivalent implementation of the Java virtual machine along with a suitable subset of the Java class libraries. There are 12 primary class "packages" defined, which collect and organize similar and related class libraries. Each package contains multiple interfaces and class libraries, each of which in turn contains multiple fields and class methods.

The Enterprise Java application environment is specified to contain all of the defined Java class libraries. The PersonalJava application environment is smaller, but still requires the complete implementation of its fixed set of class libraries to be fully compliant. The EmbeddedJava application environment, on the other hand, allows developers to include or exclude class libraries according to the requirements of a specific Internet appliance or embedded device application. The upshot is that the EmbeddedJava specification can be implemented differently from one Internet appliance or embedded device to the next, so a full implementation of the platform is not expected.

Vendors of Java technology have taken two different approaches in providing Java class libraries. Some vendors supply only a subset of the Java class libraries, which causes these implementations to be incompatible with the standard Java specifications. Other vendors supply all of the class libraries and allow the developer to select which optional libraries are required for their applications. When it comes to comparing the memory footprint of the various Java and Java-like platform implementations, you'll need to consider which class libraries are supplied to make a true apples-to-apples comparison.

{pagebreak}Evolution of the virtual machine

The virtual machine (VM) is not a new concept or practice, although the state-of-the-art in computing power has made new applications for virtual machines more viable. More than 20 years ago, IBM pioneered much of the early development of virtual machine technology.

The VM operating system for IBM computer systems is one of the best examples of the first implementations of the virtual machine concept. IBM's objective was to logically partition a single physical computer system into multiple virtual machines to create the appearance that each user had his or her own complete and separate computer system. The solution was to develop an abstraction that provided an exact duplicate of the underlying machine. Each user ran their own virtual machine completely isolated from all other virtual machines so there were no security problems. This application of virtual machine technology was well suited to the time-sharing requirements of large mainframe computers.

More recently, a new application of virtual machines has come into fashion as a means of solving system compatibility problems. For instance, there are many applications available for DOS/Windows on Intel-based systems that users of non-Intel based systems would like to be able to run. The solution was to create a PC virtual machine for the non-Intel-based systems that worked within these constrained resources. A DOS/Windows application is run on this virtual machine, and its Intel opcodes are translated into the native instruction set.

As with all virtual machines, the Java virtual machine defines an abstract computer. Its specifications define the functionality that every Java virtual machine must provide, but allow almost unlimited freedom to the designers of each implementation. For example, each Java virtual machine can use any technique to execute Java bytecodes. In fact, the Java virtual machine can be implemented in software or hardware, or varying degrees of both. This flexibility in the specification for the Java virtual machine was intended to allow for implementation on a wide variety of computers, Internet appliances and embedded devices.

,b>Class file loading and execution

At the top level, a Java virtual machine's primary purpose is to load Java class files and execute them. The Java class loader subsystem is responsible for locating and importing the bytecodes for classes. In addition, it must also verify the correctness of loaded classes, allocate and initialize memory for the class fields, and provide partial resolution of symbolic references.

Depending on a particular application's requirements, or the network environment, conceptually some or all of the Java class loader subsystem may not be required. For example, Java class files that are loaded over a network down to a Java platform-enabled computing device are generally assumed to be "untrusted." Since it is unwise to allow the Java application from an unknown source to run on your computing device without being validated, the verifier is a critical component of this Java virtual machine.

On the other hand, if you only download Java applications from your corporate server, the "screening" that is normally performed by the verifier may not be required, and the verifier itself can be configured out of this implementation of the Java virtual machine. At the far end of the functionality spectrum, the entire class loader subsystem may be completely removed. Of course, this also eliminates the opportunity to run dynamic Java applications on the computing device, but this may be totally appropriate for certain classes of embedded devices.

Similar to the class libraries discussed previously, the degree of completeness will affect the memory footprint of the various Java virtual machine implementations. Vendors of Java virtual machines have taken slightly different approaches in providing this functionality.

Sun Microsystems, and its licensees of Java technology (including Insignia Solutions), provide a complete set of functionality and allow the developer some flexibility in configuring the required Java virtual machine. Other non-licensed vendors may choose to implement and provide whatever portions of the subsystem they feel is required. Given the wide diversity of requirements for Internet appliances and embedded devices, it would seem a full-featured Java virtual machine with the maximum flexibility to configure would be most ideal.

During execution, there are two aspects of any Java virtual machine implementation that have the most impact on the suitability of Java technology for the specific application: 1) the methods used for executing the bytecodes, and 2) the management of system resources - primarily memory. Unlike the previous examples of Java class libraries and the Java class loader - where functionality may be reduced in order to shrink the size of the memory footprint for the Java virtual machine - the subsystems for the execution of bytecodes and the management of memory must be functionally complete for all implementations.

Three important attributes that have a significant affect on the final application will be determined by how well the vendor of the Java virtual machine has implemented bytecode execution and memory management. The first attribute, which is mostly affected by the methods used for bytecode execution, is performance. The second, which we have discussed considerably, is the memory footprint.

The final attribute of the application, which is subject to how well the vendor has implemented the memory management subsystem, is reliability. Taken together then, bytecode execution and memory management have the greatest effect on the suitability of Java technology for Internet appliance and embedded device development - is it fast enough, small enough, and reliable enough in its behavior for my application?{pagebreak}Compiled versus interpreted technology

The default method for executing bytecodes by a Java virtual machine is interpreted execution. Whereas C/C++ applications are compiled to a native instruction set, and are stored in a single executable file, Java applications are compiled to Java bytecodes, and are stored in separate class files for loading and execution by the Java virtual machine. When the Java application is run, the Java virtual machine loads the class files and interprets the bytecodes as a stream of instructions.

Typically, performance will be 10 to 20 times slower when the Java virtual machine uses an interpreted-only method for execution, compared to an equivalent natively compiled C/C++ program. However, there are other techniques that can improve the speed of bytecode execution. For example, just-in-time (JIT) compiling can enhance application performance up to 10 times over interpreted-only execution.

Rather than only interpreting bytecodes, each time the Java virtual machine encounters new bytecodes for the first time, it invokes the JIT compiler to compile that code to native instructions. These native instructions are then stored by the Java virtual machine and are reused the next time the code is required by the Java application. Performance comes at a cost, however. One of the major advantages of an interpreted-only version of a Java virtual machine is that it generally has a relatively small memory footprint - implementations are typically 1/2 to 3 MB of ROM, depending on which class libraries are included.

JIT compilation technology, on the other hand, typically requires at least four times the amount of memory of an interpreted-only approach. In addition, it compiles all code that it encounters, which requires unbounded amounts of memory, and often requires disk and virtual memory for paging the compiled code segments. Because of this, JIT-based execution is suitable for computing platforms that have powerful CPUs, substantial system memory (> 32 MB), and generally a fast disk drive to support swapping. It is not suitable, however, for the vast majority of Internet appliances and embedded devices that are generally far more resource constrained.

Dynamically compiled versus a JIT

A bytecode execution method more suitable for Internet appliances and embedded devices is adaptive dynamic compilation. Similar to a JIT, adaptive dynamic compilation utilizes on-the-fly compilation technology to improve the performance of interpreted bytecode execution.

Unlike a JIT, however, adaptive dynamic compilers are generally smaller in size (less than 100 KB); much more selective of the Java bytecodes that they compile to native code; do not require virtual memory; and adapt to the available system memory. During interpreted execution of the bytecodes, the Java virtual machine will monitor the application and determine where the execution bottlenecks reside. It will then invoke the adaptive dynamic compiler - which ideally is implemented as a thread - to compile the segment of bytecodes, which are executing repeatedly.

Depending on the implementation, the adaptive dynamic compiler may compile the entire class, a single method, or only a block within a method. The resulting native instructions are stored in RAM only for fast access. The Java virtual machine may then find and invoke compilation for the next most frequently executed code segment and this process is repeated until all available code buffers have been used.

Because the dynamic compiler is adaptable, as it encounters a new code segment that is executing at greater frequency than some previously compiled and stored code, it will compile these new Java bytecodes and store them in a code buffer containing less frequently used code. The user can configure the amount of system memory used for compilation, as well as the size and number of code buffers used for storing the compiled native instructions. Applications may also be given explicit control over the adaptive dynamic compiler's thread priority during execution for greater reliability, providing more suitable behavior for Internet appliances and embedded devices.

{pagebreak}Compiled versus interpreted technology

The default method for executing bytecodes by a Java virtual machine is interpreted execution. Whereas C/C++ applications are compiled to a native instruction set, and are stored in a single executable file, Java applications are compiled to Java bytecodes, and are stored in separate class files for loading and execution by the Java virtual machine. When the Java application is run, the Java virtual machine loads the class files and interprets the bytecodes as a stream of instructions.

Typically, performance will be 10 to 20 times slower when the Java virtual machine uses an interpreted-only method for execution, compared to an equivalent natively compiled C/C++ program. However, there are other techniques that can improve the speed of bytecode execution. For example, just-in-time (JIT) compiling can enhance application performance up to 10 times over interpreted-only execution.

Rather than only interpreting bytecodes, each time the Java virtual machine encounters new bytecodes for the first time, it invokes the JIT compiler to compile that code to native instructions. These native instructions are then stored by the Java virtual machine and are reused the next time the code is required by the Java application. Performance comes at a cost, however. One of the major advantages of an interpreted-only version of a Java virtual machine is that it generally has a relatively small memory footprint - implementations are typically 1/2 to 3 MB of ROM, depending on which class libraries are included.

JIT compilation technology, on the other hand, typically requires at least four times the amount of memory of an interpreted-only approach. In addition, it compiles all code that it encounters, which requires unbounded amounts of memory, and often requires disk and virtual memory for paging the compiled code segments. Because of this, JIT-based execution is suitable for computing platforms that have powerful CPUs, substantial system memory (> 32 MB), and generally a fast disk drive to support swapping. It is not suitable, however, for the vast majority of Internet appliances and embedded devices that are generally far more resource constrained.

Dynamically compiled versus a JIT

A bytecode execution method more suitable for Internet appliances and embedded devices is adaptive dynamic compilation. Similar to a JIT, adaptive dynamic compilation utilizes on-the-fly compilation technology to improve the performance of interpreted bytecode execution.

Unlike a JIT, however, adaptive dynamic compilers are generally smaller in size (less than 100 KB); much more selective of the Java bytecodes that they compile to native code; do not require virtual memory; and adapt to the available system memory. During interpreted execution of the bytecodes, the Java virtual machine will monitor the application and determine where the execution bottlenecks reside. It will then invoke the adaptive dynamic compiler - which ideally is implemented as a thread - to compile the segment of bytecodes, which are executing repeatedly.

Depending on the implementation, the adaptive dynamic compiler may compile the entire class, a single method, or only a block within a method. The resulting native instructions are stored in RAM only for fast access. The Java virtual machine may then find and invoke compilation for the next most frequently executed code segment and this process is repeated until all available code buffers have been used.

Because the dynamic compiler is adaptable, as it encounters a new code segment that is executing at greater frequency than some previously compiled and stored code, it will compile these new Java bytecodes and store them in a code buffer containing less frequently used code. The user can configure the amount of system memory used for compilation, as well as the size and number of code buffers used for storing the compiled native instructions. Applications may also be given explicit control over the adaptive dynamic compiler's thread priority during execution for greater reliability, providing more suitable behavior for Internet appliances and embedded devices.

{pagebreak}A Java Environment for Internet Appliances and Embedded Devices

To address Internet appliance and embedded device developer concerns that Java implementations were either functionally incomplete for their use, too slow, too unreliable or too big for their application needs, Insignia Solutions released the Jeode platform and its Embedded Virtual Machine (EVM) in March, 1999. The Jeode platform is Insignia's accelerated implementation of the PersonalJava and EmbeddedJava specifications tailored for Internet appliances and embedded devices.

Insignia Solutions leveraged more than a dozen years of PC virtual machines in designing the Jeode platform. The challenge of developing an efficient PC virtual machine for the resource constrained Macintosh computing environment turned out to be Insignia's "training" for building Java virtual machines suitable for Internet appliances and embedded devices.

Dynamic Compilation for faster execution

To address the issue of "too slow," Insignia leverages its twelve years of experience to deliver adaptive dynamic compiler technology. This technology differs from JIT technology in that it requires less memory, requires no disk storage or virtual memory, and dynamically compiles only the code segments that represents the current performance bottlenecks in the Java application. The remainder of the Java application code continues to be interpreted.

As the application runs, the Jeode EVM determines which code segments are executing most frequently, then compiles and stores them into the configured amount of code buffer memory. Once the allotted code buffers are used, the Jeode EVM may recycle the buffers to optimize performance in the given memory footprint.

For Internet appliances and embedded devices, it is critical this dynamic compilation process not block the main application. Therefore, the adaptive dynamic compiler operates as a thread and is preempted by any higher priority threads in the system. Based on third-party benchmark tests, the Jeode adaptive dynamic compiler provides an average of six times the Java application performance of an interpreted-only Java virtual machine, and runs in roughly one-quarter the memory footprint of a JIT-based Java virtual machine.

Precise concurrent garbage collection for more reliable behavior

The second concern expressed by Internet appliance and embedded device developers is that current implementations of the Java specifications are too unreliable for their applications. Reliability is largely determined by the "garbage collection" (GC) strategy that is used to manage memory.

Insignia believes the key to appropriate memory management for Internet appliances and embedded devices is to provide the proper core garbage collection technology and give the application control over when this process executes. Therefore, Insignia has implemented precise garbage collection technology that is fully concurrent, and provides memory compaction to eliminate memory fragmentation. The garbage collector runs as a preemptible thread and its priority can be set by the application. Unlike a conservative garbage collector, the precise garbage collector frees up all unreferenced objects, and avoids memory leaks.

To further improve reliability, the amount of memory used by the Jeode EVM can be bounded, and the usage of memory can adapt dynamically depending on the current needs of the application. For example, the garbage collector can shrink the heap to make more system memory available for dynamic compilation. In addition, each of the Jeode EVM components gracefully copes with any failure to acquire more memory. If a memory request fails, for example, the Jeode EVM may throw an exception that can be appropriately managed, rather than having the system crash.

Fully compatible Java technology

A few companies have attempted to address the issue of "too big" by removing functionality from their implementation of the standard Java specifications. Unfortunately this has two problems: 1) the Java platform principle of "write once-run anywhere" is violated, and 2) Internet appliances and embedded devices have a very wide range of functional needs, and it is impossible for the Java platform vendor to guess what is required for all applications. Insignia's approach to solving this problem is to provide developers with a completely functional and compliant version of standard Java technology, and tools to configure and tune the Jeode EVM for their specific Internet appliance or embedded device applications.

Developers can use these tools to optimize for size, functionality, and performance. Depending on the functionality selected, and required performance objectives, developers can configure the Jeode EVM that runs in as little as 358 KB, and scales up to a full configuration at 2.7MB of ROM, including all Java class libraries and required dynamic link libraries for English-only support. Full International language support requires approximately 5 MB of ROM.

Platforms supported

Insignia's Jeode software is currently available for Windows CE, NT and NT Embedded, VxWorks, ITRON and Linux operating systems. It is supported on x86, ARM, Hitachi SH, MIPS, and PowerPC processors. Other OS/processor platforms may be made available by customer request. In addition, source code and porting kits are available for customers that require other platforms for their Internet appliance and embedded device applications.

Related links:

Java Essentials for Embedded Networked Devices - Part 1: Windows and Linux set up | Part 2: Object-oriented techniques | Part 3: Errors, exceptions and exception handling | Part 4: Network programming | Part 5: Threads | Part 6: Serial ports Embedded Java Web services for smart objects - Part 1: Overview | Part 2: Performance considerations | Part 3: A real-world web service system for smart objects Rise of the Embedded Internet

Comments

blog comments powered by Disqus