Compilation Guide
Sun Java Real-Time System 2.2
  

This document describes how the developer can control the execution of the compilation system of the Sun Java™ Real Time System (Java RTS) 2.2.

Technical Documentation: Links to all the Java RTS technical documents

Contents

Introduction

Quick Start: Minimum Recommended Configuration

Just-In-Time Compilation

Initialization Time Compilation

Specifying the Methods for Initialization Time Compilation
Specifying the Classes to be Preinitialized
Generation of the Lists
Managing the ITC Lists and Files
How Does ITC Work?
Detecting JIT-Compiled Methods
Compilation Modes for the Thread Types
NoHeapRealtimeThread
RealtimeThread
java.lang.Thread
Execution Time Based on Compilation Mode
Execution of Precompiled Code
Execution of Code With Asynchronous JIT Compilation
Execution of Code With Synchronous JIT Compilation

Debugging

Compiler-Related Command-Line Parameters

Optimization Command-Line Parameters

Complete Example of ITC Usage

Abbreviations Used in This Document


Introduction

In a Java HotSpot™ virtual machine, the compilation of bytecode into native code is normally transparent to the developer. The primary performance criterion is the average execution time of applications, which translates to throughput for server-like systems. Certain VM operations can be allowed to execute in an unpredictable amount of time if they do not occur often and if this allows the most common cases to execute faster. The gain on the average execution time is positive with such a policy. For example, in the case of compilation, since compiled code executes faster than interpreted code, the overall program can run faster if frequently executed pieces of code are compiled during program execution, even if this means suspending the application during the compilation.

However, in a real-time environment, execution must be deterministic, that is, response times should be guaranteed and predictable. Variations in response time are generally of more concern than execution speed.

Determinism can be expressed in terms of jitter, where jitter measures the variation of response time or execution time. One of the most common sources of jitter is run-time compilation. If compilation interrupts the execution of a section of time-critical code, determinism is seriously compromised.

Another source of unpredictable compilation-related jitter is the on-demand resolution of symbols (fields, classes and methods). When an unresolved symbol is encountered during the execution of the code, the VM uses some extra, non-deterministic, code to resolve the symbol. In this way the VM avoids resolving symbols that are never actually used. However, since the timing of the resolution operation itself is unpredictable, this is unacceptable in time-critical code.

This document will explain how to control these two sources of jitter.

Java RTS currently supports two compilation modes: Just-In-Time Compilation (JIT) and Initialization Time Compilation (ITC).

  • In the Just-In-Time (JIT) compilation mode, compilation is triggered during the execution of the program, when certain internal counters reach specified limits. This mode is designed to be optimal in a non-real-time system.

  • Since run-time compilation (JIT) cannot be controlled, Java RTS provides the Initialization Time Compilation (ITC) mode. This mode allows you to control when specified classes will be compiled, thereby ensuring that compilation will not interfere with the execution of critical sections of code.

[Contents]


Quick Start: Minimum Recommended Configuration

Note: (Solaris OS) In order to execute the 64-bit Java RTS VM, add the -d64 option to the command line, as described in Executing the 64-Bit VM in the Java RTS Installation Guide.

As explained in the previous section, the JIT compilation scheme, which is the default for a Java program, can break the determinism of a real-time application. Therefore, you need to configure your real-time application to use the ITC scheme. This scheme is very powerful and very flexible, which means that it is also fairly complicated to understand and use to its fullest advantage. The ITC scheme is described in detail in the rest of this document.

However, for purposes of getting off to a quick start with Java RTS, you can use the following minimum configuration (with the default behavior) that is recommended for assuring determinism. You will execute your program with a command that will automatically build two files:

  • A compilation file (or precompile file), containing a list of all the methods (including the thread types for which they execute) that will be precompiled, that is, compiled when their classes are initialized. This avoids the jitter caused by runtime compilation.
  • A preinitialization file (or preinit file) containing a list of all the classes that will be preinitialized at the startup of the VM. This avoids the jitter caused by lazy initialization and ensures that the precompilation is performed early.

The precompile and preinit files are cumulative, that is, each time you run the application, more methods and classes can be added to the files. Therefore, you should run the application as many times as necessary, for example, using different input parameters or data, to ensure that all the time-critical real-time code has been executed, which ensures that the precompile and preinit lists are complete.

You can use the following command as an example of a minimum level of ITC configuration:

java \
-XX:+RTSJBuildCompilationList \
-XX:+RTSJBuildClassInitializationList \
-XX:CompilationList=<your-precompile-file> \
-XX:PreInitList=<your-preinit-file> \
<your-application> <arguments>

Here is the explanation of what is happening with the example command above:

  • With the RTSJBuildCompilationList command-line option, Java RTS automatically generates a list of the methods that were invoked by real-time threads (RTTs or NHRTs). This is the precompile list and is written to a file specified with the -XX:CompilationList option.

  • With the RTSJBuildClassInitializationList command-line option, Java RTS automatically generates a list of the classes that are referenced in the compiled code. This is the preinit list and is written to a file specified with the -XX:PreInitList option.

  • The precompile and preinit lists that Java RTS has generated are used in each run of the same application in order to reduce jitter.

  • The first time you run the program, Java RTS updates the existing precompile list (if the specified <your-precompile-file> exists) or generates the initial precompile list (if the specified file does not exist).

  • During the second run, based on the compilation of the methods in the precompile list, Java RTS updates the existing preinit list (if the specified <your-preinit-file> exists) or generates the initial preinit list (if the specified file does not exist).

  • At the beginning of the third run, when the VM starts up, Java RTS preinitializes the classes in the preinit list and precompiles the methods in the precompile list. Now, when the application executes, it should be more deterministic.

  • However, as mentioned above, you should run the application as many times as necessary to ensure code coverage. If you do change the input variables or data, be sure to run the program at least three times, as above, in order to account for any new code that was executed so that the precompile and preinit files are complete. This is the way to make sure that all real-time code is precompiled.

  • If you run a different application, remember to remove or rename the precompile and preinit files, as they are cumulative.

Note that you can use the -XX:+UseITC option as shorthand for the four command-line options in the example above. However, this option uses the default precompile and preinit file names, namely nhrt.precompile and itc.preinit. Here is an example:

java \
-XX:+UseITC \
<your-application> <arguments>

See a full example in Complete Example of ITC Usage for a sample program, including code and commands.

See also the Java RTS Quick Start Guide

[Contents]


Just-In-Time Compilation

Just in Time (JIT) compilation is the usual, transparent, compilation mode of a Java virtual machine. JIT compilation selects the methods to be compiled based on the frequency of their execution.

The section Compilation Modes for the Thread Types explains how to enable and disable the JIT compilation mode for the different thread types.

The basic principle of JIT compilation in Java RTS is the same as in the Java HotSpot VM. Compilation of a method is triggered in two situations:

  • When an interpreted call is executed, one of the first operations is to increment the invocation counter for the called method. If the counter's value reaches the compilation threshold, then the method is compiled before being executed.

  • The number of loop iterations in the method is tracked by a backedge counter. When this counter reaches a threshold value, compilation is triggered. This process is also known as On-Stack Replacement (OSR).

The time at which the compilation occurs is totally under the control of the virtual machine and cannot be predicted in 100% of the cases. For example, if a section of critical code is only executed in rare conditions, the invocation counter of a method in that code may reach the threshold after a very long running time. However, there is no way to know when the compilation will occur, and this uncertainty is not acceptable in a real-time application.

Note that, if most of the methods of an application are interpreted, the CPU load can be so heavy that compilation is delayed forever. In that case, you should compile at least a few methods.

Invocation Counter Overflow: Asynchronous or Synchronous Compilation

Compilation that is triggered by an overflow of the invocation counter can be asynchronous or synchronous. Asynchronous compilation is enabled by default in order to improve determinism. It can be disabled on the command line with the flag -XX:-BackgroundCompilation.

In asynchronous (or background) compilation, the compilation of the called method is initiated, but the thread that initiated the compilation is not blocked waiting for the compilation to complete; the thread continues executing the method in interpreted mode. Compilation continues asynchronously, in the background. After the compilation is complete, subsequent invocations of that method will execute the compiled version. However, remember that while methods are being interpreted, the CPU load can be so heavy that compilation is delayed forever.

In synchronous compilation, the thread that initiated the compilation is blocked until the method is compiled. After the compilation is complete, the thread executes the compiled method. This improves throughput earlier in the execution, but the application pauses during the compilation, and this can impact determinism.

Warning: Using synchronous compilation might introduce unbounded pause time.

[Contents]


Initialization Time Compilation

A Java program is not usually deterministic during its entire execution, that is, only certain parts of it are deemed time-critical. It is convenient to refer to a real-time application as running in different phases:

  • Initialization phase and reconfiguration phase
  • Time-critical phase

During the initialization and reconfiguration phases, the application does not need to execute deterministic code, and these are the phases where operations that potentially have an unpredictable execution time should be executed.

With the Initialization Time Compilation (ITC) mode, specified methods of a program are compiled during the initialization of their classes, that is, in advance of the time they would be compiled in JIT mode. In this way you can ensure that compilations of critical code occur during the initialization phase.

The section Compilation Modes for the Thread Types explains how to enable and disable the ITC compilation mode for the different thread types.

Class initialization is non-deterministic, because it is executed at the first use of the class. Hence class initialization should be executed during the initialization phase of the application. Therefore, class initialization is a natural place to spend CPU time compiling critical code.

As a result, if you ensure that all the classes that will be used during the time-critical phase are initialized before entering this time-critical phase, then this also ensures that all the requested compilations are performed before entering the time-critical phase. This contributes to the determinism of the application.

A class is said to be used during the time-critical phase either if it is referenced from some bytecode executed during the time-critical phase or if it contains a method that is executed in the time-critical phase.

You can control three aspects of ITC:

  • Specification of the methods to be compiled at class initialization time, by thread type
  • Specification of the classes to be preinitialized

These specifications can be performed either on the command line at VM start-up or programatically during runtime with the Compiler class in the extended RTSJ package. The ability to specify these methods and classes during execution is especially useful in user-defined class-loader-based architectures, where some methods might be written after the application has started running.

[Contents]

Specifying the Methods for Initialization Time Compilation

With ITC, you provide the descriptors of the methods to be precompiled in a list that is used to specify the methods that must be compiled when the class is initialized. This is the compilation (or precompile) list.

This list contains a method description on each line in the following format:

<class name> <method name> <method signature>  <thread type>

The <class name> is the fully qualified name of the class that defines the selected method, the <method name> is the name of the method, and the <method signature> is the signature formatted as specified in the Java Virtual Machine Specification.

It is possible to use wild cards in the <class name>, the <method name>, and the <method signature>. See the next subsection Using Wild Cards in the Compilation List for a complete description, with examples.

The <thread type> can have one of the following values:

nhrt

NHRT - NoHeapRealtimeThread

rtt

RTT - RealtimeThread

jlt

JLT - java.lang.Thread

allrt

NHRT and RTT

rttjlt

RTT and JLT

nhrtjlt

NHRT and JLT

all

All thread types: NHRT, RTT, and JLT

<empty string>

Empty string is equivalent to allrt

Note that the empty string supports earlier versions of the compilation list, although a warning message is generated. If no thread type is specified, then the system triggers the precompilation of the specified method for both RTT and NHRT.

The <thread type> variable specifies the types of threads for which the methods will be precompiled. For example, if the <thread type> variable is nhrt, this means that the method (or methods) that are specified on that line in the precompile file will be precompiled only for NHRT threads.

Important: If a particular method (that is, the same class, method, and signature) matches more than one line in the compilation list, then the method will be precompiled for all the thread types specified in all the matching lines. For example, if a method matches a line with the <thread type> equal to rtt and another line with the <thread type> equal to jlt, then the method will be precompiled for both RTTs and JLTs.

Here is a simple example of a compilation list:

java/io/PrintStream	println	()V                    rtt
java/io/PrintStream	println	(Ljava/lang/String;)V  rtt
java/io/PrintStream	write	([BII)V                rtt

More examples are shown in the subsection below Examples of Compilation List.

You can specify the list in two ways:

  • If you specify this list at VM start-up, it is passed to Java RTS in a file whose name (with its absolute path) is specified on the command line, as in the following example:

    java <vm-options> \
    -XX:CompilationList=<compilation-file-name> <your-program>
    

    Instead of manually creating the compilation list, you can ask Java RTS to do it for you. See Generation of the Lists for details.

  • You can specify this list programmatically during runtime with the mark() method of the Compiler class.

Using Wild Cards in the Compilation List

The full grammar for the syntax of the compilation list is the following:

<line>	     ::= <class name> <method name> <signature> <thread tuple>

<class name> ::= <name> | <name>'*'

<method name>::= <method name VM> | '*'

<signature>  ::= <signature name VM> | '*'

<thread type>::= 'nhrt' | 'rtt' | 'jlt' | 'allrt' | 'rttjlt' | 
                       'nhrtjlt' | 'all' | <empty string>

<name>       ::= 'a name that is acceptable for a class or method 
                  as specified in the VM specification'

<signature>  ::= 'a signature as specified in the VM specification'

The <class name> is a fully qualified class name, with the forward slash character ("/") as separator.

The asterisk (*) is the wild card. As you can see from the grammar, you can use the wild card after the beginning of the class name, in place of the method name, and in place of the method signature. See the next subsection for some examples of using wild cards.

Examples of Compilation List

The following are some examples of the compilation list, including the use of wild cards.

  • java/util/Vector indexOf (Ljava/lang/Object;)I rtt

    This line will trigger the compilation of the specified method with the specified signature in the specified class for RealtimeThread instances.

  • java/util/Vector indexOf * rtt

    This line will trigger the compilation of all the methods in Vector that have the indexOf name (all signatures) and will generate code for RTT only.

  • java/util/Vector * (Ljava/lang/Object;)I rtt

    This line will trigger the compilation of all methods with the specified signature in the specified class for RealtimeThread instances.

  • java/util/Vector * * rtt

    This line will trigger the compilation of all the methods of the Vector class for RealtimeThread instances.

  • java/util/Vector * * allrt

    This line will trigger the compilation of all the methods of the Vector class for both NoHeapRealtimeThread and RealtimeThread instances.

  • java/util* * * allrt

    This line will trigger the compilation of all the methods of all the classes of the packages that start with java/util. (For example, java/util, java/util/concurrent, java/util/concurrent/atomic, java/util/concurrent/locks, java/util/jar, and so forth.)

  • * * * all

    This line will compile all the methods of all the classes that are ever initialized by the VM and for all the thread types. This should be used with caution!

  • java/util/Vector indexof (Ljava/lang/Object;)I

    Since there is no thread type specified (empty string), this line will trigger the compilation of the specified method for both RTT and NHRT (the semantics of earlier version of the ITC system).

For a full example of generating and using the compilation list, first see Generation of the Lists for a description of the automatic generation of the list, and then see Complete Example of ITC Usage for a sample program, including code and commands.

[Contents]

Specifying the Classes to be Preinitialized

To guarantee determinism, you must preinitialize the classes that will be later used in the time-critical phase. To do so programmatically, you can use the following call to load and initialize a specified class:

java.lang.Class.forName(<class name>,true,<class loader>);

You can choose to make as many calls to this method as you have time-critical classes.

This scheme has a drawback: the initialization code must be changed each time the time-critical code is modified in a way that adds a new class.

To facilitate your work, you can ask Java RTS to initialize classes at a specified time. This is done by providing a file that contains a list of the names of classes that must be preinitialized.

You can specify the list in two ways:

  • If you specify preinitialization at VM start-up, provide a file with a fully qualified class name on each line and indicate this file (with its absolute path) on the command line, as in the following example:

    java <vm-options> \
    -XX:PreInitList=<preinit-file-name> <your-program>
    

    Instead of manually creating the preinit list, you can ask Java RTS to do it for you. See Generation of the Lists for details.

  • You can specify this list programmatically during runtime with the preinit() method of the Compiler class.

Warning: In some rare cases, the order in which static initializers are executed may be of some importance, and executing them in an arbitrary order may generate problems. Even though this is considered bad programming practice, some legacy code may cause problems of this kind.

For a full example of generating and using the preinit list, first see Generation of the Lists for a description of the automatic generation of the list, and then see Complete Example of ITC Usage for a sample program, including code and commands.

[Contents]

Generation of the Lists

Java RTS can help you reduce your work and eliminate oversights in creating the lists of methods and classes that might be needed at run-time. You can make a preliminary run of your program and request Java RTS to generate the compilation list and preinit list for you. Then you can use these lists as input to final run of your program.

All these files are stored in the directory that was the current directory when you launched the VM.

Generation of the Compilation (Precompile) List

When you run Java RTS with the option -XX:+RTSJBuildCompilationList, the VM generates a file containing a list of all the methods that were interpreted or compiled by a thread whose compilation policy is ITC. (The default name for this output file is nhrt.precompile, but you can specify a different name with the -XX:CompilationList option.) This file can then be used as input to the final run, specifying the file name with the -XX:CompilationList option on the command line.

Note that an entry for a method in the file indicates the thread types for that method. Let's take an example where a method was interpreted or compiled for a RealTimeThread and also for a NoHeapRealTimeThread, and that ITC is in force for these two thread types. In this case, the entry in the compilation file for this method will contain the indication allrt to indicate that the method should be precompiled when it is in either an RTT thread or an NHRT thread.

Caution. You should not use this option in combination with the -XX:+ITCJLT option, because the result would cause the precompilation of an enormous number of methods, which is probably not what you want. If you want to precompile some methods that are executed by java.lang.Thread instances, then you should specify these methods in a precompile list instead of requesting the automatic generation of a list of all methods executed by JLTs.

Generation of the Preinit List

When you run Java RTS with the option -XX:+RTSJBuildClassInitializationList, the VM generates a file containing a list of all the classes that were referenced during the compilation of methods at initialization, in the order in which they were initialized. The order might be important, depending on the way the static initializers were written. (The default name for the output file is itc.preinit, but you can specify a different name with the -XX:PreInitList option.) This file can then be used as input to the final run, specifying the file name with the -XX:PreInitList option on the command line.

You can also combine both the creation of the lists and the use of the lists on the same command line. Let's take the compilation file as an example. If the file does not exist or is empty, Java RTS will generate it during the run; you might experience some jitter in this case, because the frequently-executed methods were not precompiled. But during the next run, Java RTS will use the file that was generated in the previous run in order to precompile the classes containing those methods. By specifying the file generation and file use in the same command, you reduce the risk of forgetting a step. Moreover, the generated file is cumulative: if additional methods are executed in the second run of an application, these methods are added to the list of methods that were written to the file during the first run.

Below is an example of combining the creation of a list and the use of the list in one command:

java <vm-options> -XX:+RTSJBuildCompilationList \
-XX:CompilationList=<your-file-name> <your-program>

You can safely leave these options on the command line for each execution: they have an effect only at the shutdown of the VM and therefore have no impact on the performance of your application.

Warning: In order for the generated lists to be complete, you must make a set of runs that cover all of the time-critical code. Statement coverage might not be enough in cases where the application makes use of the polymorphism of method calls, which is encouraged by object-oriented design. Branch or more general path coverage might be necessary, depending on the complexity of the design. In addition, the -XX:+LogJitterProneEvents command-line parameter tracks down methods that are executed interpreted; see the Debugging section.

For a full example of generating and using the compilation list and preinit list, see Complete Example of ITC Usage for the sample program, including code and commands.

[Contents]

Managing the ITC Lists and Files

The ITC system manages two lists (precompile and preinit), which are written to two different files. These files have default names, although you may specify your own name.

When you specify a file name on the command line, you must specify the absolute path to the file. Be sure to provide write access to the files and the corresponding directory

These files are cumulative, that is, during each execution of the program, information is added to the files. For example, if you execute your program with a precompile file already populated with methods, and you request the automatic generation of the precompilation list, then the names of any new methods that are executed are added to the file, and the previous method names are retained. No data is lost.

At VM startup, any existing precompile or preinit file is copied to a backup file, in the same directory, with the extension .bak appended to the file name.

[Contents]

How Does ITC Work?

As described in the section Generation of the Lists, you can request Java RTS to automatically generate any or all of the lists for you. In this case, you must execute your program at least three times before determinism can be guaranteed.

Let's take the example of generating the precompile and preinit files with the following command:

java <vm-options> -XX:+RTSJBuildCompilationList \
-XX:+RTSJBuildClassInitializationList \
-XX:CompilationList=my-precompile-file \
-XX:PreInitList=my-preinit-file \
my-program

The first time you run the program, Java RTS updates the existing precompile list (if the specified my-precompile-file exists) or generates the initial precompile list (if the specified file does not exist). During the execution, Java RTS adds an entry in the list whenever a method is run by a real-time thread (RTT or NHRT). The entry includes the class name, method name, signature, and thread type.

During this first run Java RTS also adds entries in the preinit file for classes containing the methods in the precompile file, but the preinit file is probably not complete at this time. (It adds entries to the preinit list if the specified my-preinit-file exists, or generates the initial preinit list if the specified file does not exist.)

During the second run, Java RTS compiles the methods in the precompile list as their classes are initialized. It also updates the existing preinit list with the names of classes that are referenced in the compiled code.

At the beginning of the third run, when the VM starts up, Java RTS preinitializes the classes in the preinit list and precompiles the methods in the precompile list. Now, when the application executes, it should be more deterministic. Be sure to run the application as many times as necessary to ensure code coverage, that is, to make sure that all code branches have been executed. All the classes referenced by the real-time threads should be preinitialized, and all the methods in those classes that are invoked by real-time threads should be precompiled when their classes are preinitialized. Therefore, there should be no jitter due to compilation or symbol resolution in the real-time code during program execution.

In addition, you should run your program as many times as necessary to guarantee that all the real-time code has been executed so that all the classes and methods that are referenced by the real-time threads are included in the lists. For example, you might want to run the program with different input variables or data. Each time that you change a variable, be sure to run the program at least three times so that the precompile and preinit lists include any new classes or methods that were referenced in the newly executed real-time code.

In this example, we are assuming the default behavior, that is, that ITC is enabled for RTTs and NHRTs, but not for JLTs.

See also the full example in Complete Example of ITC Usage for a sample program, including code and commands.

[Contents]

Detecting JIT-Compiled Methods

One way to determine if you need to add more methods to the precompile list is to add to the command line the option -XX:+PrintCompilation. With this option, a message is printed every time a method is JIT-compiled, and you can examine the list to see if certain of those JIT-compiled methods should be ITC-compiled.

Note: This command only reports methods that were actually compiled; there can be additional methods that should be ITC-compiled, but were not compiled at all during that run and are therefore not detected with this option.

The output includes an identifying compilation number, the full method name (but not the signature), the total size of the bytecode, and a time stamp. After the compilation number, and before the method name, five character positions contain additional information about the compilation, where the symbols have the following meanings:

Position

Symbol and Meaning

1

'%' - OSR compilation.

'*' - Native method call wrapper compilation.

blank - Normal method compilation.

2

's' - The method is synchronized.

blank - The method is not synchronized.

3

'!' - The method has exception handlers.

blank - The method does not have exception handlers.

4

'b' - Blocking, that is, the thread which had the counter overflow is waiting for the compile to finish before continuing execution.

blank - Not blocking.

5

'R' - Compilation is for a RealtimeThread.

'N' - Compilation is for a NoHeapRealtimeThread.

blank - Compilation is for a non-real-time Java thread.

The following is some example output from the -XX:+PrintCompilation option.

103   b N  Deterministic::computeFibs (46 bytes)
 date(200947316498777)
104   b R  Deterministic::computeFibs (46 bytes)
 date(200947318378861)
105  !b N  Deterministic$RealTimeFibonacciLoops::run (164 bytes)
 date(200947367899360)
106  !b R  Deterministic$RealTimeFibonacciLoops::run (164 bytes)
 date(200947372600193)
107   b N  javax.realtime.HighResolutionTime:: (5 bytes)
 date(200947378047277)
108   b R  javax.realtime.HighResolutionTime:: (5 bytes)
 date(200947379054777)
109   b N  javax.realtime.HighResolutionTime::getMilliseconds (5 bytes)
 date(200947379923277)
110   b R  javax.realtime.HighResolutionTime::getMilliseconds (5 bytes)
 date(200947380791777)
111   b N  javax.realtime.HighResolutionTime::getNanoseconds (5 bytes)
 date(200947381593610)
112   b R  javax.realtime.HighResolutionTime::getNanoseconds (5 bytes)
 date(200947382381860)
113*  b N  javax.realtime.HighResolutionTime::relwait0 (0 bytes)
 date(200947383141693)
114*  b R  javax.realtime.HighResolutionTime::relwait0 (0 bytes)
 date(200947383838027)

This output shows, for example, that compilation number 106 had exception handlers, was blocking, and was for a RealtimeThread. Compilation number 113 was wrapping a native method call, was blocking, and was for a NoHeapRealtimeThread.

Note that each type of thread has its own copy of the compiled code. For example, RealtimeThreads execute only code compiled for RealtimeThreads, not code compiled for other types of threads. In the example output above you can see that the same method was compiled once for NoHeapRealtimeThreads and again for RealtimeThreads.

[Contents]


Compilation Modes for the Thread Types

The different types of threads in a real-time application are instances of java.lang.Thread, javax.realtime.RealtimeThread, and javax.realtime.NoHeapRealtimeThread. These thread types have different restrictions to the level of jitter that they can generate and still respect real-time requirements. In order to balance the trade-off between jitter and throughput, it is important to consider the compilation modes that are appropriate for each thread type. The choice of compilation mode might also depend on whether you use the Serial GC or the RTGC.

NoHeapRealtimeThread

Determinism is important for instances of NoHeapRealtimeThread (NHRT), and therefore ITC is enabled by default. This means that NHRT-type methods in the compilation list are precompiled at initialization time; other NHRT-type methods are interpreted.

ITC can be disabled for NHRTs with the -XX:-ITCNHRT flag. However, you should consider not executing any NHRT code as interpreted, because the best jitter numbers are obtained with compiled code.

JIT compilation is not allowed for NHRT instances.

RealtimeThread

For instances of RealtimeThread (RTT), the situation is a bit more complex than for NoHeapRealtimeThreads.

Determinism is also important for RTTs, and therefore ITC is enabled by default. ITC can be disabled for RTTs with the -XX:-ITCRT flag.

However, RTTs must balance throughput and determinism. Therefore, JIT compilation is also enabled by default. JIT can be disabled for RTTs with the -XX:-JITRT flag.

When ITC is enabled for RTTs, the methods in the compilation list are compiled at class initialization. All other methods are initially interpreted, and then they are either JIT compiled if JIT is enabled for RTTs (-XX:+JITRT), or interpreted forever if JIT is disabled for RTTs (-XX:-JITRT).

When ITC is disabled for RTTs, RTT instances execute code either JIT compiled if JIT is enabled for RTTs (-XX:+JITRT), or interpreted forever if JIT is disabled for RTTs (-XX:-JITRT).

With the RTGC, we recommend asynchronous JIT compilation, and this is the default mode for RTTs. This compilation mode will produce code that runs faster at a later time, after the compilation finishes, but there is no spike in the execution time (see graph). This mode usually does not endanger determinism if interpreted code in this application was fast enough for the particular deadline.

With the Serial GC, users may want to turn on synchronous JIT compilation. (This is accomplished by disabling the default asynchronous JIT compilation with the -XX:-BackgroundCompilation command-line option.) However, this mode of compilation is not recommended with the RTGC because the thread that triggers the compilation is blocked during compilation. This causes a spike in the execution time (see the graph), and this breaks the determinism requirement. In fact this can create unbounded jitter for all the threads, which is unacceptable in a real-time system.

java.lang.Thread

For instances of the java.lang.Thread (JLT) class, throughput and start-up time are more important than determinism. As a consequence, by default ITC is disabled and JIT compilation is enabled. Note that the only way to disable JIT compilation for JLTs is to run the application in interpreted mode (-Xint).

Unlike with the Java HotSpot VM, with Java RTS the JIT compilation is asynchronous by default. Synchronous compilation is dangerous because a JLT blocked on compilation while holding a critical lock could delay an RTT. If you do not use shared locks, or if you use the Serial GC, you can enable synchronous JIT with the flag -XX:-BackgroundCompilation.

ITC can be enabled for JLTs with the flag -XX:+ITCJLT. Using ITC for JLTs can help performance. If you know in advance the behavior of an application with respect to JIT compilation, then you can further speed up the application by specifying the methods to be compiled at class initialization (ITC). This reduces the time spent executing interpreted code. For applications that have a short run time, this almost always provides a gain in performance.

When ITC is enabled for JLTs (-XX:+ITCJLT), the methods in the compilation list are compiled at class initialization, and all other methods are JIT compiled.

When ITC is disabled for JLTs (-XX:-ITCJLT), all JLTs execute code compiled in JIT mode.

[Contents]


Execution Time Based on Compilation Mode

This section shows graphically the effect of the compilation mode on execution time, as a method is invoked a number of times. In each graph, the x-axis represents the number of times the method is invoked, and the y-axis represents execution time. As you can see on the y-axis, precompiled code takes the least amount of execution time, interpreted mode takes more, and compilation itself takes even more execution time.

Execution of Precompiled Code

In the figure below, a method is compiled at initialization time (ITC).

Figure 1 Execution of Precompiled Code

[Graph showing that execution time for precompiled code is unchanging over time]

Regardless of the number of times the method is invoked, the execution time remains the same. Therefore the jitter is theoretically zero (in practice, very small). The method is not interpreted, and no compilation takes place during execution.

Execution of Code With Asynchronous JIT Compilation

The next figure shows what happens with asynchronous JIT compilation.

Figure 2 Execution of Code With Asynchronous JIT Compilation

[Graph showing that, with asynchronous JIT compilation,
execution time goes down when compilation finishes]

In the beginning of the run, the method is executed in interpreted mode until JIT compilation is triggered by one of the internal counters. The method that triggered the compilation continues executing the method in interpreted mode, while the compilation runs in the background (asynchronously). When the compilation finishes, subsequent invocations of the method will execute the compiled code, and execution time will be lower.

In this case, the maximum jitter is the difference between the execution of the interpreted code in the beginning and the later compiled code. But this jitter is known and predictable.

Execution of Code With Synchronous JIT Compilation

In the figure below, synchronous JIT compilation has been specified (by disabling the default asynchronous JIT compilation with the -XX:-BackgroundCompilation command-line option).

Figure 3 Execution of Code With Synchronous JIT Compilation

[Graph showing that, with synchronous JIT compilation,
execution time spikes during compilation]

In the beginning of the run, the method is executed in interpreted mode until JIT compilation is triggered by one of the internal counters. This compilation causes a spike in the graph for the execution time, because the thread that triggered the compilation is blocked while the compilation runs (synchronously) to completion.

The maximum jitter is the difference between the CPU time to perform the compilation itself and the CPU time to execute the compiled code. This jitter is unpredictable in two ways: we cannot know when the compilation will occur, nor how much time it will take, because other compilation requests might be enqueued. Since this could potentially seriously disrupt a critical phase of the application, this situation is unacceptable in a real-time system.

[Contents]


Debugging

The -XX:+LogJitterProneEvents command-line option produces a log file of time-stamped events that can cause jitter. One of the events tracked is the beginning of the interpreted execution of a method. This information can help you debug the generation of the compilation list.

With this option, the VM also logs two other events: entry into and exit out of steady mode. The VM is in steady mode when it is in a time-critical execution phase, that is, a phase where it should not execute a non-deterministic operation. Entry into and exit out of steady mode can be marked by making calls to methods in the SteadyMode class in the extended RTSJ package. Having such events logged can help you determine whether or not an event that causes jitter occurred in a time-critical phase.

See the Command-Line Options section of the Java RTS Tools, Tuning, and Troubleshooting Guide for a description of the log file produced by the -XX:+LogJitterProneEvents command-line option.

[Contents]


Compiler-Related Command-Line Parameters

These parameters are also listed in the Java RTS Options document.

The table below summarizes the command-line parameters that are used to control the compilation options for the various thread types. These parameters are intended either for a production environment or a development environment, as indicated in the "Env" column.

Parameter

Default

Env

Description

ITCJLT

false

prod

When true, the ITC policy applies to all java.lang.Thread instances:

  • Methods in the compilation list are compiled at class initialization.
  • All other methods are JIT compiled.

Caution: You should not use this option in combination with the RTSJBuildCompilationList option (a request to Java RTS to automatically generate the precompile list). See Generation of the Lists for details.

When false, all java.lang.Thread instances execute code compiled in JIT mode.

ITCRT

true

prod

When true, the ITC policy applies to all javax.realtime.RealtimeThread instances:

  • Methods in the compilation list are compiled at class initialization.
  • All other methods are JIT compiled if the JITRT flag is on, or interpreted if the JITRT flag is off.

When false, all RealtimeThread instances execute code compiled in JIT mode if the JITRT flag is on, or interpreted code if the JITRT flag is off.

ITCNHRT

true

prod

When true, the ITC policy applies to all javax.realtime.NoHeapRealtimeThread instances:

  • Methods in the compilation list are compiled at class initialization.
  • All other methods are interpreted.

When false, all NoHeapRealtimeThread instances execute interpreted code.

JITRT

true

prod

When true, RealtimeThread instances execute code compiled in JIT mode, except for methods in the compilation list.

When false, RealtimeThread instances execute interpreted code, except for methods in the compilation list.

BackgroundCompilation

true

prod

When true, enable background (asynchronous) JIT compilation.

When false, disable background (asynchronous) JIT compilation. This option enables synchronous compilation. This mode is not recommended when the RTGC is used.

RTSJBuildCompilationList

false

prod

Instruct the VM to generate a file containing all the methods that were executed (compiled or interpreted) by a thread whose execution policy is ITC.

CompilationList

nhrt.precompile

prod

Name of the file containing the results from the RTSJBuildCompilationList option.

RTSJBuildClassInitializationList

false

prod

Instruct the VM to generate a file containing all the classes that are referenced during the compilation of methods at initialization. Classes appear in the order in which they were initialized during the test run.

PreInitList

itc.preinit

prod

Name of the file containing the results from the RTSJBuildClassInitializationList option.

UseITC

false

prod

Force the automatic generation and use of the precompile and preinit lists, using the default file names. When true, this option is the equivalent of specifying the following four options on the command line.

  • RTSJBuildCompilationList
  • RTSJBuildClassInitializationList
  • CompilationList=nhrt.precompile
  • PreInitList=itc.preinit

PrintCompilation

false

prod

Log JIT-compilation of methods.

PrintITCWarnings

false

prod

Print ITC-related warnings that occur during processing of class and method lists.

[Contents]


Optimization Command-Line Parameters

These parameters are also listed in the Java RTS Options document.

The Java RTS virtual machine can be configured to run with or without the Real-Time Garbage Collector (RTGC). If the RTGC is not used, then the default HotSpot serial collector is used. (The RTGC is enabled by default but can be disabled with the command-line parameter -XX:-UseRTGC.)

Java RTS uses two optimizations for virtual/interface calls to non-final methods:

  • A virtual/interface call that can be resolved to a single method at the time of the compilation might be inlined. This optimization only applies to JIT-compiled code, which means that methods in the ITC precompile list are not affected.

    If a later class loading invalidates this inlining assumption, a deoptimization is triggered, causing some jitter. You can monitor deoptimization with probes in the jrts DTrace provider, as described in the Java RTS DTrace Provider Guide.

  • The first time a virtual/interface call is performed, the call is converted to a direct call (inline cache) that is resolved at runtime. The call might later be converted to a megamorphic call, causing some jitter.

These optimizations might impact the balance between throughput and determinism and might cause extra jitter in the steady state of the application. The -XX:+LogJitterProneEvents option can provide some information, for example, when interpreted execution occurs. See the Debugging section for more information.

Virtual method call inlining (also known as Class Hierarchy Analysis, or CHA) is enabled by default for java.lang.Threads (JLTs) and RealtimeThreads (RTTs) and can be fully disabled with the option -XX:-UseCHA. When the RTGC is enabled, CHA can be selectively disabled for JLTs or RTTs with the options -XX:-JLTCHA or -XX:-RTTCHA, respectively.

Inline cache calls are enabled by default for JLTs and RTTs and can be disabled as follows:

  • When the Serial GC is used, this optimization can be disabled with the option -XX:-UseInlineCaches.
  • When the RTGC is enabled, this optimization can be disabled with the option -XX:-RTSJUseInlineCaches.

Note that these optimizations do not concern NoHeapRealtimeThreads (NHRTs).

The following table lists the command-line options that Java RTS provides to control the use of these optimizations, as described above. All these parameters are intended for a production environment.

Parameter

Default

Env

Description

UseCHA

true

prod

Use virtual method call inlining. If an inlining is invalidated because of dynamic class loading, the code is deoptimized.

JLTCHA

true with RTGC

(NA with Serial GC)

prod

For JLTs only, use virtual method call inlining when the RTGC is enabled.

(This option is not applicable with the Serial GC.)

RTTCHA

true with RTGC

(NA with Serial GC)

prod

For RTTs only, use virtual method call inlining when the RTGC is enabled.

(This option is not applicable with the Serial GC.)

UseInlineCaches

true with Serial GC

(NA with RTGC)

prod

Use inline caches when Serial GC is enabled.

(This option is not applicable with the RTGC.)

RTSJUseInlineCaches

true with RTGC

(NA with Serial GC)

prod

Use inline caches when RTGC is enabled.

(This option is not applicable with the Serial GC.)

The following table summarizes the use of these parameters, where "Y" means that the parameter can be used for a particular combination of thread type and GC; otherwise, the parameter is not applicable for that combination.

Parameter With Serial GC With RTGC
  JLT RTT NHRT JLT RTT NHRT
UseCHA Y Y --- Y Y ---
JLTCHA --- --- --- Y --- ---
RTTCHA --- --- --- --- Y ---
UseInlineCaches Y Y --- --- --- ---
RTSJUseInlineCaches --- --- --- Y Y ---

[Contents]


Complete Example of ITC Usage

This section contains a complete example of using ITC with a sample program.

  1. Create a file named HelloRealtimeWorld.java and enter the following code for the class:

    import javax.realtime.RealtimeThread;
    
    public class HelloRealtimeWorld extends RealtimeThread {
    
        String message;
    
        public void run(){ 
    	message = "Hello Realtime World";
        }
        
        public static void main(String args[]){
    	HelloRealtimeWorld thread = new HelloRealtimeWorld();
    	thread.start();
    	try{
    	    thread.join();
    	} catch(InterruptedException ie){
    	    System.err.println("got interrupted");
    	    return;
    	}
    	System.out.println(thread.message);
        }
    }
    
  2. In the directory where the class is stored, compile the class:

     javac -classpath /opt/SUNWrtjv/jre/lib/rt2.jar HelloRealtimeWorld.java
    
  3. Execute the class twice with the following command line options:

     /opt/SUNWrtjv/bin/java -XX:+RTSJBuildCompilationList \
     -XX:+RTSJBuildClassInitializationList -XX:CompilationList=itc.precompile \
     -XX:PreInitList=itc.preinit HelloRealtimeWorld
    
  4. You should now have two files in your directory:

    • A file named itc.precompile, with the following content:

      java/lang/Thread        exit    ()V     rtt
      com/sun/rtsjx/CyclicCleaningThread      run1    ()V     rtt
      com/sun/rtsjx/CyclicCleaningThread      access$000      ()V     rtt
      com/sun/rtsjx/CyclicCleaningThread$1$1  run     ()V     rtt
      HelloRealtimeWorld      run     ()V     rtt
      java/lang/ThreadGroup   remove  (Ljava/lang/Thread;)V   rtt
      
    • A file named itc.preinit, with the following content:

      java/lang/String
      java/lang/Object
      java/lang/ThreadGroup
      java/lang/Thread
      java/security/AccessControlContext
      java/lang/ThreadLocal$ThreadLocalMap
      HelloRealtimeWorld
      com/sun/rtsjx/CyclicCleaningThread
      
    • The third run should be deterministic, that is, the classes referenced by the real-time threads should be preinitialized, and the methods in those classes that are invoked by real-time threads should be precompiled when their classes are preinitialized. Therefore, there should be no compilation-induced or symbol-resolution-induced jitter in the real-time code during program execution.

[Contents]


Abbreviations Used in This Document

ITC - Initialization Time Compilation

Java RTS - Sun Java™ Real-Time System

JIT - Just In Time compilation

JLT - an instance of the java.lang.Thread class.

NHRT - No Heap Realtime Thread; an instance of the NoHeapRealtimeThread class.

RTGC - Real-Time Garbage Collector

RTT - Realtime Thread; an instance of the RealtimeThread class.

RTSJ - Real-time Specification for Java

[Contents]

Copyright © 2009 Sun Microsystems, Inc. All Rights Reserved.