Certain VMs, and Java™ VMs (JVMs) in particular, have provided a technique to allow third party applications to operate on mobile devices, such as mobile telephones. In some cases the VMs do not operate as interpreters, but instead execute native code.
By way of introduction, “bytecodes” are the machine language of the JVM. When a JVM loads a class file, it obtains one stream of bytecodes for each method in the class. The bytecodes streams are stored in a method area of the JVM. The bytecodes for a method are executed when that method is invoked during the course of running the program. They can be executed by interpretation, by just-in-time (JIT) compilation, or any other suitable technique
A method's bytecode stream is a sequence of instructions for the JVM. Each instruction consists of a one-byte opcode followed by zero or more operands. The opcode indicates the action to be taken take. If more information is required before the JVM can take the action, that information is encoded into one or more operands that immediately follow the opcode. Each type of opcode has a mnemonic. In the typical assembly language style, streams of Java bytecodes can be represented by their mnemonics followed by any operand values. The bytecode instruction set was designed to be compact. All instructions, except two that deal with table jumping, are aligned on byte boundaries. The total number of opcodes is sufficiently small that opcodes occupy only one byte. All computation in the JVM centers on the stack. Because the JVM has no registers for storing arbitrary values, everything must be pushed onto the stack before it can be used in a calculation. Bytecode instructions therefore operate primarily on the stack.
Both “green” and “native” threads are mechanisms to support multi-threaded execution of Java™ programs. Certain JVM distributions include an option to run with either type of threading (JVM can be obtained as part of a Java Development Kit (JDK), or as part of a Java Runtime Environment (JRE)). Native threads use the operating system's native ability to manage multi-threaded processes and, in particular, they use the pthread library. When running with native threads, the kernel schedules and manages the various threads that make up the process.
In contradistinction to native threads, green threads emulate multi-threaded environments without relying on native OS threading capabilities. Green threads run code in a user space that manages and schedules threads. Green threads are useful for enabling Java™ to operate in environments that do not have native thread support.
Differences between native and green threads that arise in a Linux™ environment include the following. Native threads can switch between threads preemptively, switching control from a running thread to a non-running thread at any time. In cooperative threading, threads only switch when control is explicitly given up by a thread (Thread.yield( ), Object.wait( ), etc.), or when a thread performs a blocking operation (read( ), etc.). In general, if green threads are executing and the are interpreted the VM can schedule threads between executing two bytecodes. However, if green threads are executing native code then thread execution can be interrupted only at the mentioned points. In addition, on multi-CPU machines, native threads can run more than one thread simultaneously by assigning different threads to different CPUs. Green threads run on only one CPU. Further, native threads create the appearance that many Java processes are running as each thread takes up its own entry in the process table.
The use of green threads implies that the VM implements the threads and schedules them. However, when using green threads full preemption has not been possible with native code. Native code can be either code called with a native interface, such as JNI (Java Native Interface), or code generated by a JIT, a Dynamic Adaptive Compiler (DAC), or an Ahead of Time compiler (AOT). Although in some parts of an application the multitasking may be pre-emptive, in those parts where native code is used the multitasking is either lacking or is cooperative.
It is noted that the DAC is a runtime compiler, as is JIT. One distinction between JIT and DAC is that JIT compiles all methods, whereas DAC profiles the executable code and only compiles the most-often used methods. For the purposes of this description both may be referenced simply as runtime compilers.
When the VM is using green threads, typically the VM scheduler schedules the threads after interpreting some number of bytecodes. When bytecodes need to access system services then typically native code is needed for accessing services offered by the native platform. In Java™ this native code can be used through JNI. The performance of the VM can be accelerated by compiling the most frequently used methods to native code and then executing the native code.
However, a problem that arises with native code and green threads is that the VM does not have control over the native code while it is executing. Multi-threading with green threads is only pre-emptive while executing in an interpreted mode. This means, for example, that the presence of an infinite loop in native code could prevent all other threads from executing.
One potential strategy in VMs is to use native threads instead of green threads, since the scheduling is done by the operating system (OS) and not the VM. This makes the preemption the responsibility of the operating system.
Another option is to instrument the native code with checks as to whether it should yield to thread scheduling. However, this technique requires additional coding complexity.