Java for embedded devices – then and now

When you search for “embedded Java” or “Java embedded” you will end up with a couple of Wikipedia entries and a link to the Oracle website that provides very generic information about “Java SE Embedded” version 8 - period. Mainstream Java is heading towards version >20 nowadays … so what’s going on with Java in the embedded space?

Let’s start with a quick look back in time. The technology called “Java” was initially developed by a couple of smart engineers at Sun Microsystems during 1991/92. Originally it was developed to provide an object oriented and hardware/OS independent programming and runtime environment for embedded systems. The idea was to create a uniform and portable programming platform with automatic memory management and robust execution of code in a virtual environment. By using a VM (virtual machine) programmers would automatically avoid otherwise common problems like crashes caused by buffer overflows and faulty pointer arithmetic.

Object oriented programming for embedded systems was a relatively new idea in the 90ies – and the embedded community was very skeptical. The Java programming language was designed to be interpreted during runtime and as such intrinsically slow and resource hungry. And even though the first Java release only had 8 packages and about 200 classes it was rendered too heavy by many hardcore C/C++ and assembler programmers. Given the limited processing power and very limited memory availability back in the day, it was understandable that the advantages of object oriented programming, portability and secure execution did convince a lot of software engineers.

By the way, there are a number more or less credible stories why the technology was called “Java” in the first place. One is that the developers met on a coffee shop close to their office and drank predominantly a coffee blend called Java, another story is that the name is derived from the very first application implemented with the technology – a coffee machine. Anyway – the bad taste of being slow and resource hungry stuck with Java forever.

Settop box manufacturers were among the earliest adopters of Java in the embedded space even before the Java plugin for web browsers opened the door for an entirely new programming model on the desktop. Applets, small mostly visual programs written in Java, could be downloaded into a browser and create an interactive user experience that was previously hard to accomplish with HTML and early scripting. And since the Java code was executed in a virtual machine invoked by the browser, applet authors didn’t have to care a lot about the variety of target operating systems and underlying CPU architectures. Java grew bigger and stronger and conquered the desktop world. PCs even back then had serious computing power and memory and some technology advances like just-in-time (JIT) compilers held to increase the acceptance of Java as a mainstream programming language for the client.

Programmers and project managers realized the advantages of object oriented programming and the benefits of fast growing class libraries and functionality included in the Java language. But most embedded systems still weren’t strong and big enough to host a full desktop Java runtime environment (JRE). The Java stakeholders (Sun Microsystems, IBM, Nokia, RIM, Phillips, Siemens, Motorola and others), organized in the “Java community process” approved a “Java Specification Request” (JSR) #68 in the year 2000 to specify a Java variant specifically designed for embedded use: the Java Micro Edition / J2ME. Subsetted class libraries and small footprint VMs opened the door for widespread use of Java in embedded systems. In particular mobile phones made use of Java with a the so called MIDP profile, a configuration targeted to handheld phones with elementary graphics capabilities (LCDUI).

More and more embedded systems were integrated into communication networks and implementing complex networking protocol in C or even assembler turned out to be complicated and error prone. Java offered an integrated networks stack and an automatic software distribution mechanism locally and over the network. And embedded JVMs became available for a large number of operating systems and CPU architectures like SH4, PowerPC, ARM, MIPS and x86.

The adoption of Java in the embedded space was still limited by constant complaints about poor runtime performance and high RAM/RAM requirements. But new technologies like tiered garbage collectors and ahead of time compilation (AOT) made the execution of Java code more predictable and faster than ever. And the advent of stronger 32bit processors and affordable memory opened the way for many high tech usecases like automotive headunits and GSM network stations and controllers.

Java surpassed all other languages in popularity in 2001 when it became the most used programming language according to the TIOBE Index for the first time and stayed on top until 2019. During that period it got widespread adoption in devices like telematics units, Blu-ray players, and of course smartphones. The first incarnation of Android was essentially an embedded Linux system with an integrated virtual machine that was able to execute Java code. Practically all apps had to be written in Java – but it is interesting to note that Android did not fully implement the Java specification as defined by the JCP. One of the initial promises of Java “write once, run anywhere” (also often dubbed as “write once, test everywhere”) was already somewhat eroded with the embedded profiles, but Google’s move fragmented the Java world even further – and played a major role in a year long lawsuit between Google and Oracle, which had acquired Sun Microsystems in 2009.

Java is still among the most popular languages today and embedded application benefit from the typed language and the integrated security measures in the current Java systems. But it looks like the idea of “embedded Java” came to an end with Java 8. In an attempt maintain the integrity of the Java language and still allow for customizing Java runtimes for embedded systems, Oracle release the “Java Embedded 8” product which defined three so called compact profiles, strict subsets of the desktop class libraries. This move was needed since Java 8 had become pretty huge and contained many features rarely used in embedded systems. The smallest compact profile class libraries have a footprint of less then 14Mb as opposed to the desktop version libraries that go beyond 50 Mb. A configurable JIT compiler and a choice of garbage collectors complemented the embedded version and made it a suitable embedded platform for many industries – for example the world’s largest automakers relied on Java technology for their infotainment and telematics systems.

Since Java is currently on release level 19 – it is a fair question to ask: why there is no more recent Java embedded version?

First of all, the embedded systems market is very complex. There are hundreds if not thousands of different CPU variants and operating system dialects on the potential target devices and it is extremely expensive to maintain a complex codebase like a VM on so many platforms. And the few profitable high volume embedded applications like smartphones have either turned to open source offerings or focus on different languages. So it is hard to justify big investments in a large number of virtual machine platforms. IBM had withdrawn already from the embedded market and stopped releasing their embedded Java product unofficially called “J9” with the end of the JavaME aera. Oracle has also cut down the number of supported platforms step by step. What remains is a number of smaller software vendors (like MicroDoc) who specialize in customization and optimization of JVMs for niche markets.

Another argument why there is no specific embedded version comes for the advances of the Java platform itself. Since Java 9, a new module system (known as “Jigsaw”) was introduced. The monolithic class library was rearchitected to allow separation into functional components that can be added as needed for a runtime system. Unused components can be left out – footprint reduced. Whether this technology really lives up to its promises remains to be decided by the embedded systems engineers. Some claim the unbundling was not done thoroughly enough and the essential core modules needed for every application are still too big.

And still, of course, people complain about performance issues – in particular startup time. And there’s appoint to it. When you launch a VM, you load a big piece of software and classes before any user code can be executed. And then the JIT compiler is monitoring the application and decides to interrupt the execution of frequently used methods to compile them into machine code. That helps at runtime later, but it also slows down the systems total startup.

Help comes from an unexpected direction: from the cloud.

Cloud computing became a mainstream business in recent years. Giant server farms host applications for millions of users. Many services offered in the cloud are based on microservice architectures. Microservices are small functional entities that are invoked and immediately suspended after use. It is an interesting fact that the requirements from cloud computing are pretty in line with what’s needed in the embedded space: Small footprint and fast startup.

Even though today’s servers have abundant horsepower and virtually unlimited memory, when you serve millions of users at a time, there resources need to be share between all users, and the fraction available for a single user can become pretty small. And as a user you don’t want to wait for many services to startup slow – you want immediate response.

Oracle is one of the major cloud providers on the planet and has launched a game-changing project to solve these problems: GraalVM.

GraalVM is a portable virtual machine that can be used to execute a variety of programming languages: Python, R, Ruby, JavaScript and Java. And GraalVM offers a unique feature called “Native Image” that can be used to compile Java applications directly into an executable for a target platform. The so called “native image” is different from a VM executing AOT code. It only contains ready to run machine code of the application and a lightweight memory manager for garbage collection. Most of the other heavyweight VM components are stripped: no interpreter, no JIT compiler, no class libraries are part of the native image. This leads to a super slim footprint and blinding fast startup times.

GraalVM “native image” is an ideal tool bring new and existing Java application to embedded devices. It offers the full universe of Java advantages and at the same time saves memory and CPU cycles. Oracle is focusing on server based cloud computing with GraalVM but there is a growing community working on implementations for embedded use, for example MicroDoc who have implemented a crosscompiler for native image compilation that can create executables for previously unsupported platforms that are commonly used in the embedded space (like 32Bit ARM). Very obviously, the story of embedded Java is not over, it might have just begun…..