A Practical Introduction to Achieving Determinism
Sun Java Real-Time System 2.2
  

This guide describes how to use Sun Java™ Real-Time System (Java RTS) to quickly and easily achieve determinism in a Java RTS application by eliminating the usual sources of jitter.

This document is intended for programmers with minimum knowledge of the Java™ programming language and environment, as well as the Solaris™ Operating System (Solaris OS) or the Linux Operating System.

The Practical Introduction to Achieving Determinism Annex document contains complementary, more detailed information about the elements described in this document.

Technical Documentation: Links to all the Java RTS technical documents

Contents


Introduction

The purpose of this document is to get you started, quickly and easily, using Java RTS to achieve determinism in a real-time application.

The main characteristic of a real-time application is that the system is subject to real-time constraints. Worst-case response times must be deterministic, in other words, predictable within given limits. The Sun Java Real-Time System guarantees determinism by ensuring that critical code is executed at the highest priority, even higher than the garbage collector (GC) if needed. In addition, the Java RTS implementation allows any Java™ Platform, Standard Edition (Java SE) application to run at the same time as a Java RTS application without affecting the determinism of the real-time application.

The first Java Specification Request (JSR) issued by the Java Community ProcessSM (JCP) was JSR 1: the Real-Time Specification for Java (RTSJ). The Sun Java Real-Time System (Java RTS) is Sun's implementation of that specification.

We define non-real-time and real-time requirements for an application as follows:

  • A non-real-time application has no time constraint requirements.

  • In a real-time application, response times must be deterministic, in other words, guaranteed and predictable.

    • In a soft real-time application, missing one deadline is not critical.

    • In a hard real-time application, time constraints must always be met.

Determinism can be expressed in terms of jitter, which is central to measuring the deterministic behavior of a program. Jitter measures the variation in a particular response time for a particular computational scenario of interest within a given executing application. An ideal value for jitter would be zero, indicating perfect determinism. More realistically, the aim of real-time processing is to reduce jitter to an acceptable level. Moreover, any jitter that occurs must be bounded, that is, we must guarantee that the jitter remains within known bounds.

This document is provided with a "Getting Started" package (GettingStarted.zip), which contains the example programs that support the explanations and comments. They are very simple: They iterate a predefined sequence of calls to perform calculation and memory allocation. We measure for each iteration the wall-clock-elapsed time. The jitter is defined here as the difference between the best and the worst execution time of the set of iterations when running the programs of the package.

This document has four parts:

  • The first part presents the usual sources of jitter that can occur in a Java program. See the section Usual Sources of Jitter.

  • The core of the document describes a set of scenarios executed by the script file contained in the "Getting Started" package. Each scenario shows a use case of running the example programs using either a vanilla VM (such as the Java HotSpot™ virtual machine) or the Java RTS VM. The purpose of the scenarios is to show specific situations or ways to achieve determinism using Java RTS. Each scenario includes an output example, comments, and an interpretation of the results. See the section Scenarios for the Example Programs.

  • Then the document describes how to set up the "Getting Started" package and how to run the accompanying script file that runs the scenarios. See the section Running the Provided Scenarios.

  • Finally, the Conclusion section summarizes the various situations and ways to achieve determinism that were described in the scenario section.

The "Getting Started" package contains all the elements necessary for all levels of user interest:

  • Example programs that demonstrate non-deterministic behavior, as well as deterministic behavior, with and without garbage collection. The source code and the bytecode for the example programs are included.

  • Several predefined scenarios of all the example programs, showing the effect of configuration on the determinism of the programs. For example, we configure the priorities of the various threads, the type of compilation, the load on the CPU, the load on memory allocation and garbage collection, the length of time a scheduled thread pauses (relinguishes control) to allow other threads to execute, the use of the Real-Time Garbage Collector, amount of memory reserved for activity of critical threads, and so forth.

  • The script file that automatically runs all the scenarios.

The example programs of the package are called NonDeterministic, Deterministic, and GCDeterministic.

  • The first program is a non-real-time program and executes in a non-deterministic way.

  • The second program is a real-time Java RTS program. The variation in execution time is practically zero, thus making the program predictable, that is, deterministic. This program does not consume memory in its steady state, and garbage collection never comes into play.

  • The third program brings the Java RTS Real-Time Garbage Collector (RTGC) into the picture to show how to combine real-time priorities of threads and configure the RTGC to maintain determinism.

Provided with the programs is a script that runs several scenarios by setting various program and Java RTS VM command-line parameters, in various configurations. You can run this script as-is, with no changes, to see how each configuration affects the resulting level of determinism. In addition, you can modify the script, tune the parameters, and even make changes to the programs themselves. For example, you can test your own code for determinism by hooking it into the example programs.

For more details about the programs (processing, input, output), the script file, and executing your own code, see the Practical Introduction to Achieving Determinism Annex.

[Contents]


Usual Sources of Jitter

Achieving better determinism in an application depends primarily on reducing and controlling jitter. To accomplish this, you must first be aware of what conditions can cause jitter. Jitter can be caused by many activities taking place in the system, including various VM internal activities. This section discusses the most common sources of jitter and how to deal with them.

Static Initialization

The Java VM normally initializes a class when the program first tries to make an instance of the class, or when the program tries to use a static method or variable of the class. When the class is initialized, its static variables are initialized. This activity, of course, takes execution time and can interfere with the execution of any code that is critical for the real-time application. Since you cannot predict when classes will be initialized, you cannot predict when this jitter will occur, nor whether the amount of jitter is acceptable.

However, you can specify a list of classes to be initialized before the application starts executing. In addition, you can request Java RTS to generate this list for you. See the Java RTS Compilation Guide for details.

Compilation

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

JIT is the original compilation mode for the Java HotSpot virtual machine, where compilation of a method is triggered when certain internal counters reach specified limits. In a non-real-time environment, this mode is designed to trigger compilation at optimal times. However, this mode can cause jitter in a real-time environment.

In the ITC mode, certain specified methods are marked for ITC compilation. When a class is initialized, the methods in it that are marked are compiled at that time. You specify these methods in a list that is passed as a command line argument. You can also ask Java RTS to create the list for you. By specifying that methods in critical code sections are to be compiled at initialization, you can reduce or eliminate jitter caused by run-time (JIT) compilation.

For detailed information, see the Java RTS Compilation Guide.

Garbage Collection

Java RTS supports two garbage collectors: the non-real-time serial garbage collector, and the Real-Time Garbage Collector (RTGC). When the non-real-time garbage collection occurs, the application processes are suspended.

However, if the RTGC is used, it can be configured to execute in a deterministic manner, that is, critical threads running at a higher level of priority will not be interrupted by garbage collection. The third example program illustrates the use of run-time parameters to control when garbage collection can occur, thereby putting more control over GC-caused jitter.

See the Java RTS Garbage Collection Guide for a detailed discussion.

[Contents]


Scenarios for the Example Programs

This section describes the scenarios for the three example programs, states their purpose, and comments on the results.

As mentioned above, the three example programs - NonDeterministic, Deterministic, and GCDeterministic - are intended to show various sources of jitter, and how Java RTS avoids these jitter pitfalls, using only instances of the javax.realtime.RealtimeThread class (RTT), the ITC compilation system, and the Java RTS Real-Time Garbage Collector (RTGC).

The NoHeapRealtimeThread type of thread, dedicated to hard real-time, is not used in these examples. However, we show that hard real-time can be achieved using RTTs running at critical priority (critical RTT), correctly configuring the RTGC, and using ITC compilation.

Scenarios involving the NonDeterministic program are intended to show failure of achieving determinism with a vanilla VM. In those scenarios we put the vanilla VM in a situation where class initialization jitter is avoided. This is done by executing preliminary stress with the Fibonacci and GarbageProducer classes in the static initialization code of the NonDeterministic program. This guarantees that all classes are already initialized while the program is in its steady state. For the Deterministic and GCDeterministic programs, we use the recommended way of specifying preinitialization: We build a preinitialization list that we provide at start-up of the Java RTS VM, such that all classes used are loaded and initialized before the program starts. See the Java RTS Compilation Guide for more details.

The example programs supporting the scenarios use two types of threads:

  • Bench thread: The instance of thread on which we do determinism measurements. The bench threads will be instances of java.lang.Thread (JLT) in the NonDeterministic scenarios, and instances of javax.realtime.RealtimeThread (RTT) in the Deterministic and GCDeterministic scenarios. For each scenario there is exactly one bench thread.

  • Stress thread: The instance of thread(s) that run in the program, in addition to the bench thread, to consume CPU time and memory in order to stress the system and try to break determinism. The stress threads will be either JLT or RTT, depending on the scenario. There can be at most two stress threads running in the scenarios: one JLT and one RTT. In some scenarios there is only one JLT stress thread running, and in others only one RTT stress thread.

The bench threads and the stress threads accept a user-specified pause_time parameter to define a length of time that the thread pauses in order to allow other threads (for example, garbage collection and JIT compilation) to execute. However, this pause_time is implemented differently for a bench thread and for a stress thread.

  • For the bench thread, we use wait-for-next-period logic. The program first calculates the average execution time for a certain number of iterations of the logic, including garbage collection. This is called the "stress cost." To this is added the value of the pause_time parameter, which represents additional inactive time, in order to arrive at the calculated period.
  • For the stress thread, the pause_time is the time the thread will sleep within each of the outer loops.

For all scenarios, the bench and stress threads are described, together with their type (RTT or JLT).

Be sure to refer to the Practical Introduction to Achieving Determinism Annex for more detailed explanations about the programs.

[Contents]

Multiprocessing Notes

The scenarios that are provided in the "Getting Started" package were written to be run on Solaris OS. However, the script can also run on Linux. Several of the scenarios use Solaris processor sets to take advantage of the machine architecture, and to make sure that the selected CPUs in the processor set are isolated from any other activity on the machine. The equivalent functionality on Linux includes processor affinity and cpusets, in order to restrict the set of processors used by the tests.

In addition, the processors in the processor sets are specified to be non-interruptible.

However, you can use the script as-is (on Solaris or Linux) to run the scenarios on a single-processor machine, although some of the scenarios, mainly those for the GCDeterministic program, require a 4-CPU machine. If you have a single-CPU machine, just remove all instances of the following command in the script file:

  • Solaris: psrset
  • Linux: taskset

Note that removing these commands in the GCDeterministic scenarios will affect deterministic behavior.

For information about using processor sets on Solaris OS, see the section on using multiprocessors in the Java RTS Technical Information document, as well as the article Solaris Processor Sets Made Easy.

[Contents]

Purpose of the Scenarios

The scenarios described here are those that are run when using the script file provided within the "Getting Started" package. You can play with the scenarios by changing the input parameters inside the script file and by modifying the contents of the programs. The impact of those changes will help you observe and understand real-time behavior, but is not mandatory. For an explanation on how to make these changes, see the section Running the Script File (in this document) and the section Testing Determinism with Your Own Code (in the Practical Introduction to Achieving Determinism Annex).

Each program and dedicated set of scenarios fulfills several objectives:

  • NonDeterministic scenarios show that you cannot obtain deterministic behavior with instances of the standard java.lang.Thread class (JLT), whether or not garbage collection (GC) activity is necessary. The output figures are, however, clearer regarding determinism failure when GC activity occurs. The 7 scenarios run on a vanilla Java VM. The last 3 of the scenarios stress GC activity.

  • Deterministic scenarios show that using RTTs will immediately provide deterministic behavior in the normal case, where all that is necessary is to change JLTs to RTTs. In addition, these scenarios show that the best deterministic behavior will be achieved using the ITC compilation system. These scenarios also show the behavior of RTTs regarding scheduling and the impact of priority. All of the scenarios are free of any GC activity.

  • GCDeterministic scenarios deal with the case where memory must be recycled, and this puts RTGC on the scene. When there are several real-time threads running together, and when garbage collection becomes necessary, the situation becomes more complex, and these scenarios illustrate some typical situations where both soft RTT and critical RTTs exist together. There are 12 GCDeterministic scenarios:

    • The first 6 scenarios illustrate how to ensure determinism with critical RTT and RTGC activity. They show that critical RTTs are never interrupted by soft RTTs or RTGC threads, except when the critical threads consume so much CPU that RTGC cannot get enough free CPU to recycle memory. The last scenarios show that one solution is to allow the RTGC to run for enough time on a free CPU.

      Most scenarios here are deterministic. Determinism is broken only when a critical RTT cannot allocate any memory because the RTGC did not recycle it in time.

      The first four of these scenarios force the run on only one processor. The last two use two processors. If you run the scenarios with the script file provided, make sure that you run them on a multiprocessor machine (more than two processors).

    • The last 6 scenarios are dedicated to soft real-time and show how we achieved determinism with soft RTTs. We start with a simple situation where the priority level can provide determinism. We then proceed, with more complicated configurations, to show how to maintain determinism by setting the pause time and letting the auto-tuning capacity of the RTGC adapt to the situation. All these scenarios are to be run on a single processor.

[Contents]

Analyzing the Output of the Scenarios

All the output is described in detail in the Output from the Example Programs section of the Practical Introduction to Achieving Determinism Annex. However, we describe here a part of the output that is used for interpreting the results. The time unit of the recorded values is the microsecond, unless otherwise indicated.

Note: The scenario results that are shown in this document were obtained on a Sun Fire™ X4100 M2, with 4 CPUs, and with Solaris 5.10 OS U4.

For all scenarios we provide only the summary section of the output results:

=====================================
Summary of results:
=====================================
Mean execution time: 377 microseconds
Best execution time: 308 microseconds
Worst execution time: 160331 microseconds
Most frequent execution time: 313 microseconds, 345 occurrences
Execution time jitter: 160023
Standard deviation: 2886.58
=====================================

This summary shows for this given run:

  • The mean execution time for all the iterations.

  • The best and the worst execution times observed.

  • The most frequent execution time value observed, together with the number of occurrences. The most frequent value, although not a statistical measure, indicates whether the run was stable in the measurements performed on the bench thread. If the number of occurrences is high, this means that were a lot of small deviations between the measurements. The higher the number of occurrences of the most frequent value, the more meaningful is the "mean execution time" value.

  • The jitter measured for the whole run.

  • The standard deviation found after the last iteration.

For a deterministic scenario, the results should be similar to the following:

  • The best and the worst execution time should be similar.

  • Jitter value should be in the hundreds of microseconds.

  • Standard deviation should be in the tens of microseconds.

[Contents]

NonDeterministic Scenarios

The script file executes 7 runs with a vanilla VM such as HotSpot, or any VM that conforms to the Real-time Specifications for Java, such as Java RTS. The first four runs avoid bringing garbage collection into play, while the rest produce some garbage that must be recycled by the garbage collector. All these scenarios run on one CPU.

The script executes these scenarios with a vanilla VM. Separate runs with Java RTS provided similar results, and these results are reported also.

Note that only the Summary section of the output is shown here, although the "Comments" often refer to detailed output that is displayed with the verbose option.

  1. Run1: One bench JLT running at max priority, one stress JLT running at min priority, no GC activity, 5000 iterations measured.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 361 microseconds
    Best execution time: 307 microseconds
    Worst execution time: 120316 microseconds
    Most frequent execution time: 312 microseconds, 1266 occurrences
    Execution time jitter: 120009
    Standard deviation: 2118.2
    ==========================================
    

    Comments: This is a very good situation for avoiding outliers. There is no memory load, and execution time is very small (no time-sharing switch). Despite this, we observe 3 very big outliers. Otherwise the collected executions are almost always the same, and there are very few deviations from the reference value of more than 5 microseconds. Java RTS results are quite similar but more stable and more fixed in the execution time.

    Interpretation: Determinism cannot be achieved with such a simple Java program, even without any memory consumption.

  2. Run2: Same as Run1, but the two threads have the same normal priority, and the execution is longer (around time-sharing switch time); 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 25116 microseconds
    Best execution time: 12356 microseconds
    Worst execution time: 562526 microseconds
    Most frequent execution time: 12438 microseconds, 18 occurrences
    Execution time jitter: 550170
    Standard deviation: 39946.29
    ==========================================
    

    Comments: If execution time is longer than the time-sharing switch time of the system, its effect immediately shows up. The two threads are scheduled intermittently around 100 times. That is the reason for these 128 outliers. Many more outliers occur and are really bad. There is a lot of variation in the execution time. Most of the deviations from the reference value are in the 0-200 microsecond range. Java RTS results are quite similar but more stable and fixed in the execution time.

    Interpretation: Execution time of the JLTs has a bad influence on the determinism since the JLTs can be rescheduled one after the other more frequently by the VM.

  3. Run3: Same as Run2, but the bench thread is run with maximum priority and the stress thread with minimum priority, trying to take advantage of priorities in the standard Java.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 15338 microseconds
    Best execution time: 12350 microseconds
    Worst execution time: 172418 microseconds
    Most frequent execution time: 12504 microseconds, 28 occurrences
    Execution time jitter: 160068
    Standard deviation: 15119.80
    ==========================================
    

    Comments: Compared to Run2, there are fewer outliers and they are shorter, but the situation has not changed overall. There is no stabilization of the behavior, and outliers still occur at the end of the run. Otherwise most of the deviations are in the 0-200 microsecond range. Java RTS results are very similar.

    Interpretation: Setting the priority of the bench thread to the maximum and the priority of the stress thread to a minimum does not bring fundamental progress. The scheduling of a JLT is based on time-sharing and even a high-priority thread can be preempted at any time by the VM or another thread.

  4. Run4: Only one bench JLT thread running at max priority, execution time around time-sharing switch time, 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 12506 microseconds
    Best execution time: 12372 microseconds
    Worst execution time: 13231 microseconds
    Most frequent execution time: 12450 microseconds, 24 occurrences
    Execution time jitter: 859
    Standard deviation: 94.89 
    ==========================================
    

    Comments: There are only 7 deviations of more than 500 microseconds, but this is the best situation that can be imagined for the VM. The jitter due to JIT is not visible here because JIT occurs in the first iteration, since we increased the number of inner loops to get the expected execution time. Thus JIT occurs almost immediately. You could lower the number of inner loops and increase the number of stress class iterations to observe in the results that the JIT happens much later in the run, causing a big outlier in the results. Deviations are much smaller and the mean value seems relevant. Most of the deviations are under 200 microseconds. Java RTS shows a very similar result, but with smaller deviations between execution times.

    Interpretation: In the best situation that be imagined for the VM (an application with a single JLT running max priority, only basic calculation without any garbage produced), correct behavior is almost reached (but still 8 deviations of more than 500 microseconds). But is such an application useful for real-time purposes?

  5. Run5: Same as Run2, with the addition of garbage production by the stress JLT.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 37717 microseconds
    Best execution time: 12315 microseconds
    Worst execution time: 432595 microseconds
    Most frequent execution time: 12491 microseconds, 32 occurrences
    Execution time jitter: 420280
    Standard deviation: 67965.83
    ==========================================
    

    Comments: The number of outliers has dramatically increased, the deviations are large, and most of the consecutive execution times show differences of greater than 100 microseconds. The price to pay for GC is really heavy regarding determinism. Java RTS presents very similar results with JLTs.

    Interpretation: Compared to Run2, the addition of a second stress JLT producing garbage makes the picture even worse. You can see in the "summary of results" section of the output that the best and most frequent execution times are comparable, but the worst execution time, the jitter, and the standard deviation have increased dramatically. Any time the GC needs to recycle memory for the stress thread to continue its job, both the bench and stress threads are stopped, and this causes a lot of outliers. This occurs even in the last loops of the run. When GC comes into the picture, there is clearly no determinism.

  6. Run6: Same as Run5, with bench thread running at maximum and stress thread running at minimum priority.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 16587 microseconds
    Best execution time: 12350 microseconds
    Worst execution time: 412557 microseconds
    Most frequent execution time: 12481 microseconds, 53 occurrences
    Execution time jitter: 400207
    Standard deviation: 24848.16
    ==========================================
    

    Comments: Deviations are still large, as shown by the 51 outliers due to GC activity stopping the bench thread. This is also shown by the jitter and standard deviation values. However, the run is more stable. Deviation between each iteration is greater than 100 microseconds. Java RTS presents very similar results for JLTs.

    Interpretation: Changing the priority of the bench thread to the maximum does not change anything. The bench thread is still preempted by the GC when recycling memory.

  7. Run7: Same as Run6, but where the stress thread does not exist. Only the bench thread is running, and it runs at max priority and produces garbage. There are 5000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 2929 microseconds
    Best execution time: 2772 microseconds
    Worst execution time: 4031 microseconds
    Most frequent execution time: 2884 microseconds, 176 occurrences
    Execution time jitter: 1259
    Standard deviation: 101.30
    ==========================================
    

    Comments: Results are much better but still not sufficient. There are 24 outliers due to GC activity stopping the bench thread. Deviations between consecutive execution times are larger. Jitter is greater than 1 millisecond.

    Even when there is only one thread running, and this one producing garbage, determinism is largely broken by GC activity when recycling memory needed for the bench thread. This happens until the end of the run. Java RTS shows very similar results on JLT threads.

    Interpretation: When memory is being recycled, the bench thread is preempted by the GC activity, which takes a long time and breaks potential determinism. This happens whenever memory becomes too low, at any time.

All these scenarios show that you cannot achieve true determinism with a vanilla VM. The separate runs with Java RTS are very similar to those with the vanilla VM in terms of throughput and behavior regarding JLTs.

[Contents]

Deterministic Scenarios

The Deterministic program can only be run on a VM that is compliant with RTSJ. Run3 is expected to run on Java RTS because it uses the ITC compilation system.

The first two of these scenarios do not use ITC compilation and allow JIT compilation to occur. The Deterministic program uses java.realtime.RealtimeThread (RTT), in addition to JLTs. There is no memory consumption by the threads run by this program, so there is no GC activity here. See the section Description of the Example Programs in the Practical Introduction to Achieving Determinism Annex for more information on what the Deterministic program does.

Note that only the Summary section of the output is shown here, although the "Comments" often refer to detailed output that is displayed with the verbose option.

  1. Run1: Runs an RTT as the bench thread running at maximum real-time priority and a JLT running at minimum priority (with Fibonacci calculations only) as a stress thread. Runs on one processor. There is a 1 millisecond pause time in the bench thread to allow JIT compilation to occur; otherwise the bench thread would take all the free CPU time and thus run in interpreted mode until termination. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 687 microseconds
    Best execution time: 687 microseconds
    Worst execution time: 728 microseconds
    Most frequent execution time: 687 microseconds, 668 occurrences
    Execution time jitter: 41
    Standard deviation: 5.12
    ===========================================
    

    Comments: As you can see in the output excerpt, the behavior is very predictable. There is no deviation larger than 500 microseconds. Most of the results are identical (687 microseconds). This reference value is found 668 times out of 1000 iterations. The worst deviation between two consecutive iterations is only 39 microseconds, and happens only once. The jitter figure (41 microseconds) and standard deviation figure (5 microseconds) are excellent. You can notice that the JLT stress thread has no influence on the RTT bench thread. This is expected, since the JLT is not executed at real-time priority.

    The Deterministic program highly favors the JIT compilation system since the executed code in the bench thread is always the same and the closure of the method calls is completely traversed at each iteration. In most applications this is not the case, and because of conditional calls and logic program some method calls will happen much later, thus creating unexpected JIT compilation jitter.

    Interpretation: It is very easy to get deterministic behavior with Java RTS using RTTs. You just have to replace java.lang.Thread by javax.realtime.RealtimeThread and you are done, if there is no memory consumption. However, some real-time applications need to consume memory inside their time-critical section, and this is why it is necessary to use either NHRTs or the RTGC. We will not address the use of NHRTs in this document. For the use of the RTGC, see the scenarios for the GCDeterministic program. We will see that we can get controlled compilation and thus totally avoid compilation-related jitter in Run3 using the ITC compilation system.

  2. Run2: Same as Run1, with an additional RTT running at minimum real-time priority (with Fibonacci calculations only) as a stress thread. This run also pauses for 1 millisecond in each iteration. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 689 microseconds
    Best execution time: 687 microseconds
    Worst execution time: 729 microseconds
    Most frequent execution time: 690 microseconds, 631 occurrences
    Execution time jitter: 42
    Standard deviation: 5.27
    ==========================================
    

    Comments: Same behavior as for Run1. Introducing the second stress RTT does not change anything. That was expected. This run shows that Java RTS follows the scheduling model of RTSJ. The stress RTT will execute only when the bench RTT is pausing and JIT compilation has finished.

    Interpretation: Java RTS scheduling follows the Real-Time Specification for Java. Thus neither the stress JLT nor the stress RTT could disturb the execution of the bench RTT. The bench RTT was scheduled to run every time it was not pausing. Setting real-time priorities in Java RTS has a real effect on the scheduling behavior of RTTs.

  3. Run3: Same as Run2, but this time we will use the ITC compilation system of Java RTS and observe the results. There are 5000 iterations. We use the precompile and preload lists.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 685 microseconds
    Best execution time: 681 microseconds
    Worst execution time: 722 microseconds
    Most frequent execution time: 686 microseconds, 1389 occurrences
    Execution time jitter: 41
    Standard deviation: 4.94
    ==========================================
    
    

    Comments: Using the ITC compilation system improves determinism. There are 1389 identical elapsed execution times out of 5000, and most of them deviate less than 5 microseconds from the reference value. The worst deviation is 33 microseconds. For a vanilla VM and JIT, there were 1110 identical results, and small deviations were constant throughout the run.

    However, the most important thing to notice is that the reference value was almost immediately measured in the run (at iteration 2), and stabilization of the run lasted from that point until its end.

    Interpretation: When there is no memory to be recycled in the RTT critical section, ITC compilation solves the remaining problems for determinism by suppressing almost all the jitter sources that are not due to GC activity. The ITC compilation system is the right way to get determinism when GC does not happen. In the following scenarios for GCDeterministic, the ITC compilation system is fully used (precompile and preinitialization list).

The simplest way to get determinism with Java RTS is to replace java.lang.Thread with javax.realtime.RealtimeThread when you do not consider GC constraints. Due to JIT compilation, there could still be some jitter if some classes are used late within the life of the RTTs. The solution for this is to use the ITC compilation system, so that you will have true determinism immediately after initialization of the application.

[Contents]

GCDeterministic Scenarios

The GCDeterministic program is to be run in all cases with Java RTS only, on a multiprocessor machine, and with the ITC compilation system activated for all classes and methods (precompile and preinit lists both used).

The provided script uses processor sets on Solaris (or processor affinity on Linux) to illustrate the distributed execution of the RTGC. You can also run the program without processor sets or processor affinity, but this will change the behavior of the scenarios depending on the number of available CPUs on your machine and the load. See Multiprocessing Notes.

See the Java RTS Garbage Collection Guide for a description of the RTGC.

These scenarios show how to use the RTGC both when hard real-time is necessary (critical bench RTT) and when soft real-time is sufficient.

Note that only the Summary section of the output is shown here, although the "Comments" often refer to detailed output that is displayed with the verbose option.

  1. Run1: One bench RTT running at maximum real-time priority (that is, a critical RTT) with no allocations. One stress RTT with many allocations and running at minimum real-time priority. There is no pause time neither for bench nor stress RTTs. The run is forced to execute on only one processor. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 173 microseconds
    Best execution time: 172 microseconds
    Worst execution time: 184 microseconds
    Most frequent execution time: 173 microseconds, 409 occurrences
    Execution time jitter: 12
    Standard deviation: 1.50
    ==========================================
    

    Comments: Hard real-time succeeds with this bench critical RTT. GC never happens while the bench RTT is running, but only when it has finished and when the stress RTT allocates too much memory. Notice that deviation to previous is zero, jitter is 12 microseconds, and standard deviation is 1.5 microsecond. These are excellent results (like Run3 of Deterministic scenarios).

    Interpretation: Again the real-time priority and scheduling model of Java RTS came into play in this run. The stress RTT allocating memory could not start before the critical bench RTT terminated. Therefore it could not be stopped for any memory recycling reason.

  2. Run2: This is the same scenario as Run1, with the bench RTT also allocating a lot of memory. Again there is no pause time for the bench and stress RTTs. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 985 microseconds
    Best execution time: 639 microseconds
    Worst execution time: 61277 microseconds
    Most frequent execution time: 683 microseconds, 38 occurrences
    Execution time jitter: 60638
    Standard deviation: 3838.29
    ==========================================
    

    Comments: This is not a deterministic run. Jitter is large, as well as the standard deviation. There are small variations in execution time (less than 10 microseconds). The bench thread is blocked 6 times out of 20 GC cycles. These outliers occur because there is no pause in the bench RTT, and therefore the RTGC cannot recycle memory. The bench thread cannot allocate any more memory and is stopped.

    Interpretation: Failure to achieve a deterministic run happened for two reasons: there was no free time left on the single CPU to let the RTGC work; and there was no RTGCCriticalReservedBytes threshold set for the bench RTT. This is what we will set in the next scenario.

  3. Run3: Same as Run2, but using the Java RTS option RTGCCriticalReservedBytes of 10 megabytes. There is no pause time within the iterations. This is executed on one processor.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 1006 microseconds
    Best execution time: 664 microseconds
    Worst execution time: 49769 microseconds
    Most frequent execution time: 683 microseconds, 53 occurrences
    Execution time jitter: 49105
    Standard deviation: 3711.62
    ==========================================
    

    Comments: The result is exactly the same as previous run. Nothing has changed. For this reason we did not include the "noticeable deviations" output.

    Interpretation: Using the RTGCCriticalReservedBytes option is the right way to allow the bench thread to continue to allocate memory when needed. But again, since it is a critical thread, the bench RTT will continue to run and will not let the RTGC work. Thus the critical reserved bytes threshold will also be reached, and the bench thread is stopped on allocation failure as in the previous run. Critical threads must let the RTGC run from time to time. The less CPU time it gets, the higher the RTGCCriticalReservedBytes threshold must be. Setting a pause time in the bench thread is a solution in this case. This is what we show in the next scenario.

  4. Run4: Now we add a small pause time in the bench thread execution. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 680 microseconds
    Best execution time: 592 microseconds
    Worst execution time: 1046 microseconds
    Most frequent execution time: 680 microseconds, 60 occurrences
    Execution time jitter: 454
    Standard deviation: 19.93
    ==========================================
    

    Comments: There is only one deviation to previous that is more than 500 microseconds. Moreover, the worst deviation is less than 500 microseconds from the reference value (366 microseconds). This is a good determinism achievement. Determinism is guaranteed, firstly by allowing a pause time in the bench thread to give the RTGC some time to work, and secondly by specifying the RTGCCriticalReservedBytes threshold (sized against the allocation rate of the bench thread). If we increase the pause time in the bench thread, the run will be even more deterministic.

    Interpretation: This run shows the correct usage of the RTGCCriticalReservedBytes option, which is very important for avoiding the previous situation for critical threads when garbage is produced.

    On a single-CPU machine, even with a very small pause time in the bench thread, setting the RTGCCriticalReservedBytes option allows the RTGC to make enough progress to avoid a memory starvation. On a multiprocessor machine, a pause time of zero will still work if the allocation rate of the bench RTT is not too high. This is because the RTGC will run on a separate CPU and thus will be able to collect memory rapidly enough. However, if the RTGCCriticalReservedBytes are consumed too fast, the critical thread will be stopped again as in Run2 and Run3.

    You can play with the pause time parameter for the bench thread and the size of the RTGCCriticalReservedBytes memory buffer to find the minimal numbers to avoid stopping the bench thread. The longer the pause time, the better determinism figures you will get, which are already excellent for a minimum pause time of 4 milliseconds. The value of the RTGCCriticalReservedBytes threshold depends on several factors: the allocation rate, the CPU time remaining for the RTGC, and the memory that can be freed by each RTGC cycle.

  5. Run5: This is the same as Run3, but this time rather than adding a pause time, we use two CPUs. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 792 microseconds
    Best execution time: 610 microseconds
    Worst execution time: 1086 microseconds
    Most frequent execution time: 771 microseconds, 17 occurrences
    Execution time jitter: 476
    Standard deviation: 83.99
    ==========================================
    

    Comments: The determinism is good. There is no deviation more than 500 microseconds. If the allocation rate of the bench thread is not too heavy, the GC will be able to use the second CPU to work and ensure determinism. This is the case here.

    Interpretation: The RTGC was able to run on the second CPU while the critical bench thread continued to run. In this case the allocation rate of the bench thread was not too huge, and the RTGC could recycle memory quickly enough to let the bench thread continue allocating. In the next run we lower the allocation rate of the bench thread.

  6. Run6: Same as Run5, except that the heap load on the critical bench thread is not heavy. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 264 microseconds
    Best execution time: 220 microseconds
    Worst execution time: 481 microseconds
    Most frequent execution time: 275 microseconds, 37 occurrences
    Execution time jitter: 261
    Standard deviation: 21.55
    ==========================================
    

    Comments: The results are even better than for Run5. The standard deviation is divided by 4, jitter is divided by 2, and mean of delta between consecutive iterations went from 30 to 15 microseconds, which is excellent.

    Interpretation: These results are closer to those that you can achieve with a real application, which would not stress the RTGC as much as in the previous run.

  7. Run7: From this run to the last scenario, we explore the tuning of the RTGC for soft real-time. So in all these scenarios, all running RTT threads have a priority that is less than the critical priority. For this first soft real-time scenario, we only change the priority of the bench thread from critical to normal, and observe the result. The run is executed on a single CPU. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 172 microseconds
    Best execution time: 172 microseconds
    Worst execution time: 206 microseconds
    Most frequent execution time: 173 microseconds, 466 occurrences
    Execution time jitter: 34
    Standard deviation: 1.85
    ==========================================
    

    Comments: The results are very similar to those of Run1. Only one deviation is 33 microseconds, with a few deviations less than 10 microseconds. Standard deviation is about 2 microseconds. This is because, even with soft real-time, the priority of the bench RTT is higher than the one of the stress RTT. Therefore this run is very similar to Run1 because of the scheduling model of RTSJ.

    Interpretation: The combined priority of the bench and stress RTTs together with scheduling model of RTSJ made this run deterministic. The stress RTT (the only one producing garbage) was not able to start before the bench thread finished. Thus the stress RTT could run alone on the single CPU without being stopped by either the RTGC or the stress RTT activity.

  8. Run8: Same as Run7, but the bench RTT is now producing a lot of garbage to recycle. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 1352 microseconds
    Best execution time: 629 microseconds
    Worst execution time: 63080 microseconds
    Most frequent execution time: 656 microseconds, 30 occurrences
    Execution time jitter: 62451
    Standard deviation: 5775.48
    ==========================================
    

    Comments: Determinism has been somewhat lost, but except for the loops where the RTGC had to come into play on the only available CPU, the run was very stable. In any case, full determinism cannot be achieved because at some time the RTGC has to collect garbage and can only do that by delaying the bench RTT. There are only 9 noticeable deviations, and they are very regular even though the allocation rate was heavy.

    Interpretation: Whenever the RTGC detects that memory is low, this means that it did not get enough CPU time to recycle memory. Therefore, the priority of the RTGC collector threads is boosted to critical in order to be able to work and recycle memory. This is done very regularly, and except for those outliers, the elapsed execution time is very predictable.

  9. Run9: We use again a critical reserved bytes size of 10 megabytes as in Run3. Otherwise the run is identical to Run8. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 1447 microseconds
    Best execution time: 626 microseconds
    Worst execution time: 47475 microseconds
    Most frequent execution time: 652 microseconds, 39 occurrences
    Execution time jitter: 46849
    Standard deviation: 5461.10
    ==========================================
    

    Comments: The behavior is exactly the same as in Run8. The RTGCCriticalReservedBytes option does not provide any advantage since the bench is not a critical thread, and so cannot take advantage of this reserved memory. Again the thread is stopped when RTGC activity comes in the picture.

    Interpretation: RTGCCriticalReservedBytes will only impact the behavior of the critical RTTs that do some allocation.

  10. Run10: We now allow the bench soft real-time RTT to have a pause of 6 milliseconds in each iteration, expecting the RTGC to take advantage and do the collection during the pause time. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 1498 microseconds
    Best execution time: 871 microseconds
    Worst execution time: 61073 microseconds
    Most frequent execution time: 918 microseconds, 82 occurrences
    Execution time jitter: 60202
    Standard deviation: 5679.13
    ==========================================
    

    Comments: Trying to let RTGC do collection when the bench RTT is idle is a good idea. The RTGC has been able to adapt to the allocation situation of the program.

    Interpretation: Even if this is not a true application, this illustrates at least the auto-tuning feature of RTGC for the soft real-time. So again, if you want to reach good determinism for soft real-time, remember that putting some pause time in the bench RTT, thus freeing CPU time for the RTGC, will help the RTGC auto-tune. This is not guaranteed, however, because the stress RTT was also able to use this pause time to run, and this degrades the auto-tuning capacity of the RTGC. So let's apply this pause time principle to both running RTTs in order to give the RTGC more chance to work.

  11. Run11: Same as Run10, but we also pause the stress RTT for 4 milliseconds in each iteration. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 872 microseconds
    Best execution time: 507 microseconds
    Worst execution time: 62287 microseconds
    Most frequent execution time: 689 microseconds, 53 occurrences
    Execution time jitter: 61780
    Standard deviation: 3370.91
    ==========================================
    

    Comments: Auto-tuning of the RTGC happens now very rapidly, after 2 GC cycles. The RTGC has been able to adapt to the situation and avoid any jitter on the bench thread after the first 180 iterations. This is a second win for the soft real-time thread.

    Interpretation: As we set also a pause time in the stress RTT, the RTGC is adapting much more quickly. A good rule of thumb to get more deterministic behavior is to ensure that the load is low enough so that your real-time threads do not use 100% of the CPU power. Again, this cannot be guaranteed and depends on the memory allocation rate of the application.

  12. Run12: Same as Run11, while using the NormalMinFreeBytes option set to 30 megabytes. There are 1000 iterations.

    Output result excerpt:

    ==========================================
    Summary of results:
    ==========================================
    Mean execution time: 674 microseconds
    Best execution time: 573 microseconds
    Worst execution time: 900 microseconds
    Most frequent execution time: 673 microseconds, 58 occurrences
    Execution time jitter: 327
    Standard deviation: 25.76
    ==========================================
    

    Comments: We now get excellent results. The RTGC now starts more quickly (as soon as the remaining free memory goes below 30 megabytes) because we set the NormalMinFreeBytes threshold. Observing the summary of results, you can see that the run is very deterministic, and the mean to delta to previous is 12 microseconds. There is no deviation more than 230 microseconds.

    Interpretation: The RTGC was able to adapt more quickly because we forced it to start sooner.

[Contents]

Conclusions from the Scenarios

The scenarios for each of the example programs have led to the following set of conclusions:

  • The NonDeterministic program showed that determinism cannot be achieved on a standard VM because of the default scheduling policy of Java and garbage collection activity, even putting the VM in the best possible situation regarding known sources of jitter.

  • Running the Deterministic program showed that determinism is immediately achieved using RTTs with Java RTS when the application does depend on memory being recycled. The recommended way to fully control or avoid class loading, initialization, and compilation jitter is to use properly the ITC compilation system.

  • The GCDeterministic program showed that critical RTTs cannot be interrupted by the RTGC. To guarantee determinism with garbage collection, configure the RTGCCriticalReservedBytes parameter to reserve some memory for the critical RTTs that you want to behave deterministically. A key point to the design of the application is to balance the number of critical threads and the CPU usage so that the RTGC will have free CPU cycles to work with. If you have too many critical RTTs, or if they allocate too much memory or use too much CPU time, determinism might be compromised. The recommended practice is to use critical RTTs for hard real-time constraints and RTTs with non-critical priority for soft real-time.

    For soft real-time RTT, the rule of thumb to achieve determinism is to make sure that the RTGC gets some free time on an available CPU in order to tune its activity based on the overall allocation rate behavior. You can cause the RTGC to start earlier by setting the NormalMinFreeBytes threshold to something different from zero. In this way, the memory recycling will be stabilized earlier. Again, the best value will be obtained by observing the application behavior regarding memory consumption.

[Contents]


Running the Provided Scenarios

This document is included in a package that allows you to execute the scenarios on your own Java RTS installation. This package consists of a jar file containing the Java bytecode of the examples, the source of the examples, and a script file that runs the scenarios exactly as described above. This section explains how to use this package. It is useful if you want to see how the scenarios behave on your own machine. You will find more details on the programs in the Practical Introduction to Achieving Determinism Annex.

Setting up the "Getting Started" Package

To run the examples of the "Getting Started" package on your Java Real-Time System, you must either run as root superuser or ensure you have the right privileges. Before trying to run the programs, see the Java RTS Installation Notes for information on correctly setting the privileges.

The examples are provided both as jar archives and source code. They are packed in the GettingStarted.zip file. This compressed archive contains the following:

  • run-scenarios.sh shell script. This shell script file will run all the scenarios, one after the other, in the same order as they are described in this document. See the section Running the Script File.

  • GettingStarted.jar Java archive. This file contains the bytecode for all the classes of the example programs. You can use it as-is to run the programs. This jar file will be an input parameter to the script file to run the NonDeterministic, Deterministic, and GCDeterministic programs.

  • src directory containing the source code for the example programs.

Before starting, change the current directory to your working directory:

$ cd <working-dir>

Then perform the following steps:

  1. Copy the GettingStarted.zip file to your working directory.

  2. Unzip the GettingStarted.zip file.

    $ unzip GettingStarted.zip
    $ ls
    GettingStarted.jar run-scenarios.sh src
  3. If you do not want to modify the source code and play with the example programs now, you have finished the set-up. You can now do either of the following:

You are free to modify the examples. Recompile the modified Java source as described in the section below.

The example programs contain hooks where you can execute your own code as a "stress class," in addition to or in place of the stress classes used in the examples. This allows you to test your own Java code for determinism. See the section Testing Determinism With Your Own Code in the Practical Introduction to Achieving Determinism Annex.

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.

Compiling the Programs

If you make changes to the programs or add your own code, compile them with the following commands:

$cd <working-dir>
$<Java-RTS-install-dir>/bin/javac \
-classpath src:<Java-RTS-install-dir>/lib/rt2.jar src/*.java

The <Java-RTS-install-dir> variable is the location of your Java RTS installation. This will compile all the source code in the src directory and produce class files that you can run manually as described below.

Running the Programs

To run the compiled program from the jar file, type the following commands:

$cd <working-dir>
$<Java-RTS-install-dir>/bin/java <java-vm-args> \
-classpath ' GettingStarted.jar | src ' \
-client \
<example-program> \
<bench-thread-parameters> <stress-thread-parameters> \
[<stress-thread-parameters>] [<verbosity-flag>]

The arguments are as follows:

  • <Java-RTS-install-dir> is the location of your Java RTS installation.

  • <java-vm-args> are any options that you want to set for this run of the VM, such as the Java RTS options for invoking ITC compilation system or for tuning the Real-Time Garbage Collector. See the contents of the provided script file and the documentation provided with Java RTS for information about all the options for Java RTS.

  • ' GettingStarted.jar | src ' : Specifying GettingStarted.jar will run the programs as-is, as they are found in the GettingStarted.jar file. Specifying src will run the programs from the class files that you may have modified and recompiled, as described in the section Compiling the Programs above.

  • <example-program> is one of NonDeterministic, Deterministic, GCDeterministic.

  • <bench-thread-parameters> and <stress-thread-parameters>, as well as <verbosity-flag>, are described in the section Input Parameters for the Example Programs in the Practical Introduction to Achieving Determinism Annex.

Be sure to refer to the Sun Java RTS Release Notes for detailed information about running Java RTS.

Running the Script File

The script file that is provided with the program jar file and source code runs the scenarios exactly as they are described in this document, and in the same order. See the section Scenarios for the Example Programs above in this document. The script is a very simple Bourne shell (sh) script that executes the sequence of instructions for each scenario. The parameters and the name of the output file are different for each scenario. Please notice that the scenarios are expected to run on a multiprocessor machine and use processor sets on Solaris OS or processor affinity on Linux. See Multiprocessing Notes.

To run the script file, do the following:

$cd <working-dir>
$./run-scenarios.sh <script-parameters>

The format of the <script-parameters> is as follows:

<vanilla-java-command> <javaRTS-java-command> \
<verbose-flag> <getting-started-jar> <optional-vm-args>

The meaning of the arguments:

  • <vanilla-java-command>: Path and binary java command for the vanilla Java VM to use.

  • <javaRTS-java-command>: Path and binary java command for the Java RTS VM to use.

  • <verbose-flag>: The value "verbose" will produce detailed output from the programs. Any other value produces a summary.

  • <getting-started-jar>: Path and filename of the GettingStarted.jar file.

  • <optional-vm-args>: Arguments that you may want to pass to the VMs used by the script file. These arguments are passed to all scenarios, and thus are passed to both vanilla Java VM and Java RTS VM. For this reason, any Java RTS VM specific argument that is passed will cause the scenarios for the vanilla Java VM to fail since they will not be recognized.

Here is an example of the script file execution:

$./run-scenarios.sh java /opt/SUNWrtjv/bin/java verbose
<full-path-to-working-dir>/GettingStarted.jar \
-XX:+RTGCPrintStatistics

Each run of a scenario within the script creates an output file with a name that reflects the parameters of the scenario, the run command, and the scenario number.

You can also modify the script file, run it, and observe the differences. The header of the script file explains how to modify it.

[Contents]


Conclusion

This document has presented, in various levels of detail, a set of example programs that you can run in order to see first-hand how to achieve determinism in a real-time program by using Java RTS. Real-time constraints imply that response times must be guaranteed and predictable, in other words, deterministic. We define determinism in terms of jitter, that is, the variation in execution time.

Provided with the document is a complete package containing the following:

  • A jar file containing the bytecode for all the example programs.

  • A script file to run several predefined scenarios for each program.

  • The source code for all the programs.

Having all this data means that you can test the programs on several levels:

The scenarios were created with the intention of demonstrating various combinations of the major parameters that can play a part in how the programs behave:

  • Type of VM (vanilla HotSpot vs Java RTS)
  • Type of thread (JLT vs RTT)
  • Level of real-time thread priority (soft vs hard/critical)
  • Thread priority value (absolute, as well as relative to the priorities of other threads, including the RTGC)
  • Compilation scheme (JIT vs ITC)
  • Time-sharing with other threads
  • CPU consumption
  • Memory consumption
  • Amount of memory reserved for critical and non-critical RTT threads
  • Garbage collection (vanilla Java VM garbage collector vs RTGC)
  • Pause time (sleep time or additional inactive time in wait-for-next-period scheme)
  • Multiprocessor environment
  • Processor sets on Solaris OS or processor affinity on Linux

Some of the Java RTS VM flags have been used for the scenarios, and some of these are mentioned in the description of the scenarios. All of these flags are described in the Java RTS documentation: Release Notes, Compilation Guide, and Garbage Collection Guide.

This Practical Introduction to Achieving Determinism document and the complementary Practical Introduction to Achieving Determinism Annex together describe how to use Java RTS to quickly and easily achieve determinism. The Annex explains exactly how the programs work, the input to the programs, and the output from the programs. This current document describes the scenarios in the script, as well as, for each scenario, the conclusions that can be drawn from the results of that run, often compared to the results from other runs. Here is a summary of those conclusions:

  • Running on a vanilla VM cannot be deterministic, because of the time-sharing policy of the Java VM.

  • A JLT cannot be deterministic, because it can be preempted by garbage collection at any moment, regardless of its priority.

  • JIT compilation occurs at unpredictable times, causing an unknown amount of jitter at unknown times during execution.

  • Using ITC and precompiling critical methods improves determinism. See the Compilation Guide.

  • Running a critical RTT on Java RTS can be truly deterministic, even with a huge CPU load, additional threads, and lots of memory consumption and garbage collection.

  • The RTGC works within the thread priorities to manage garbage collection at optimal times.

  • Priorities must be set properly to take advantage of the fact that critical threads are not preempted by other threads, even the GC.

  • If a thread has hard real-time constraints, set its priority to critical. A critical RTT thread is defined by having a priority higher than the "critical priority" of the RTGC. This means that the RTGC will not preempt these threads for garbage collection. To protect critical threads from allocations by non-critical threads, memory must be reserved using an important parameter: RTGCCriticalReservedBytes. Only critical threads will be able to continue memory allocation when the critical reserved bytes threshold is reached, and still behave deterministically if RTGC can recycle enough memory faster than the allocation rate of the critical threads. See the Garbage Collection Guide.

  • While an application is running, and consuming memory in a regular fashion, the RTGC can tune its behavior advantageously by observing the memory allocation activity and history. This self-tuning can help in achieving deterministic behavior for soft real-time threads. Using the NormalMinFreeBytes command-line parameter can speed up the self-tuning of the RTGC. See the Java RTS Garbage Collection Guide for a detailed explanation.

  • You can also specify a small pause in the critical thread to let the RTGC get some CPU time to do collection.

  • Adding processors can help an application that is already deterministic.

Based on the above discussion, the overall conclusion is that real-time determinism can be easily achieved with Java RTS by performing the following steps:

  1. For the threads that should behave more deterministically, convert instances of java.lang.Thread to instances of javax.realtime.RealtimeThread.

  2. Use ITC compilation, specifying critical methods to precompile.

  3. Use the Real-Time Garbage Collector.

  4. Correctly assign priorities, identifying the few critical threads.

  5. Tune the RTGCCriticalReservedBytes threshold value for the critical threads.

See the Practical Introduction to Achieving Determinism Annex for complementary, more detailed information about the elements described in this document.

[Contents]


Glossary

GC - Garbage Collection, or Garbage Collector
ITC - Initialization-Time Compilation
Java RTS - Sun Java Real-Time System
JIT - Just-In-Time compilation
Jitter - A measure of the variation in a particular response time.
JLT - An instance of the java.lang.Thread class.
JSR - Java Specification Request
NHRT - No Heap Real-Time Thread. This is a thread that cannot be interrupted by garbage collection.
RTGC - Real-Time Garbage Collection
RTSJ - Real-Time Specification for Java
RTT - An instance of the javax.realtime.RealtimeThread class.

[Contents]

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