Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.

Channels ▼

JVM Languages

JBED: Java for Real-Time Systems

Source Code Accompanies This Article. Download It Now.

Dr. Dobb's Journal November 1999

A JVM and an RTOS

By Jörgen Tryggvesson, Torbjörn Mattsson, and Hansruedi Heeb

Jörgen is development engineer at Mecel AB. He can be reached at [email protected] mecel.se. Torbjörn is a software consultant working at Mecel AB and can be contacted at [email protected] Hansruedi is CEO of Esmertec Inc., and can be contacted at [email protected]

Jbed is a small, fast Java Virtual Machine (JVM) for embedded real-time systems. It also includes a complete real-time operating system (RTOS). In designing Jbed, our first goal at Esmertec (http://www .esmertec.com/) was to make a seamless combination of an RTOS and JVM without any glue code in between. Our second design goal was not to rely on an interpreter or just-in-time (JIT) compilation. Instead, Jbed uses either a precompiled native image, or a built-in compiler that translates Java bytecode into native code at load time. (This technique is usually referred to as "flash compilation," but since someone trademarked that term, we call it "TBCC," short for "target bytecode compilation.")

Because Jbed always runs natively, it is a very fast Java system. That it is also a small system may be surprising, but leaving out the interpreter and not relying on techniques such as JIT saves memory. Additional memory saving is achieved by writing everything in Java -- even the device drivers (to write device drivers in Java, Jbed provides a built-in class with methods for direct memory access). Of course, you can link to legacy code written in C, but if you refrain from that, only the Java memory management has to be loaded, and a lot of overhead is avoided. A complete Jbed system with TBCC is only about 256 KB and uses about the same amount of RAM to run. A system with TCP/IP and a web server is about 128 KB. Minimal precompiled systems without the TBCC can be smaller than 10 KB. These numbers include both the RTOS and JVM.

TBCC technology has another benefit. Code loaded dynamically into the running system is compiled immediately, then executed at full native speed. This makes Jbed ideal for applications where interpreters are too slow, but where hot-loads of patches or new applications are necessary (as with some Jini applications, for instance).

A final design goal was to make Jbed suitable for hard real-time applications. Hard real-time usually means that no outside event must ever be handled too late. More precisely, each event has a given deadline and missing any such deadline results in an error condition (soft real-time, on the other hand, allows for occasional deadline misses). Real-time applications are more challenging under a Java-based operating system than under other systems because Java uses garbage collection. The garbage collector used in Sun's original JVM cannot be interrupted and is therefore unsuitable for real-time applications. Instead, an incremental garbage collection algorithm has to be used that can be interrupted anytime.

Jbed is a clean-room design and contains no Sun sources. It comes with its own integrated development environment (IDE), or can be used with Symantec's Visual Café, IBM's VisualAge, and the like. Jbed runs on Motorola 68xxx and PowerPC processors, with Intel, ARM, and MIPS processors support coming soon.

Hard Real Time

Contrary to popular notions, hard real-time problems with extensive hardware interaction can be solved entirely in Java. To illustrate this, we present a hard real-time example that is simple and maintainable, while still meeting hard real-time design requirements. In doing so, we won't link C code for the device drivers, but instead use the hardware interface libraries that come with Jbed. This not only makes memory management easier, but has advantages in terms of documentation and code maintenance. Because the problem is hard real time, Java systems that are interpreted or rely on techniques such as JIT compilation can't be used.

The problem at hand involves a robot with two moving axles, both under control of the system; see Figure 1. The robot has an attached laser-pointer that is used to display Lissajous figures on a screen (a Lissajous figure is the superposition of the two sine functions x(t)=Asin(ct) and y(t)= Bsin(dt+e) plotted in the x-y-plane; see Figure 2. This example is hard real time because the movements of the robot have to be monitored and adjusted in very short time intervals (milliseconds) to achieve the desired accuracy. The accuracy of the system can be easily tested by displaying a Lissajous figure where the two sines have the same frequency and no phase difference because the resulting figure must be a line. The slightest inaccuracy of the timing makes it into an ellipse instead. Finally, the robot system will be implemented as a time-triggered (deadline-driven) system.

Event-Triggered versus Time-Triggered Systems

In event-triggered systems (based on interrupts) that use multiple sensors (heat sensors, oil-pressure sensors, sensors triggered once per rotation of a moving part, etc.), each trigger is an interrupt handled by an interrupt routine. The interrupt routines are short, and as much work as possible is delegated to nonreal-time threads. (Here, we use the term "thread" exclusively for Java style, nonreal-time threads, and we use "task" for real-time tasks.) Each interrupt routine has a priority. Routines with higher priority will interrupt routines with lower priority.

In such a system, priorities have to be assigned (possibly dynamically) such that each external event is serviced within its deadline. For instance, the interrupt routine that must be called once per rotation of a moving part must have a priority that guarantees it is still called once per rotation even under special circumstances (such as a malfunction of another part of the machine). If this is not guaranteed, a malfunction could spread quickly. In a complex system, this priority assignment can be very difficult to debug.

In time-triggered (deadline-driven) systems, on the other hand, all sensors are sampled (polled) at regular intervals. Each sensor causes the execution of a small piece of code, similar to an interrupt routine. Again, as much of the calculation as possible is delegated to nonreal-time threads. Instead of priorities, the routines triggered by the sensors have deadlines that are usually known a priori and need not be assigned. For example, a rotating part that rotates a maximum of 1000 times per second has a natural deadline of 1 millisecond. The scheduling is based on which deadline is the most urgent (earliest deadline gets first scheduling).

Because the routines usually do not perform complex calculations, it is possible to assign them a duration (that is, a maximum execution time). Again, the duration is known a priori and, therefore, the scheduler can do a load-time analysis of the schedule of the system under all circumstances. After all modules are loaded (either statically and one-time or dynamically), Jbed guarantees that the system can be scheduled under all circumstances if the durations of the real-time tasks were given correctly. If the system cannot be scheduled, it does not even start executing. The requirement to have a correct estimate of the duration of the real-time tasks is not a serious problem in practice, because well-designed tasks are simple and the estimates can be on the safe side (and the actual durations are given in the Jbed debugger). In our robot example, it is guaranteed that the service routine for all moving parts is executed in time under all circumstances.

How does the responsiveness of event-triggered systems compare to their time-triggered counterparts? Under small loads (few external events), event-triggered systems are more responsive because each event is serviced immediately (unless a higher priority event comes in between). Time-triggered systems have a response time given by the sampling period. Under high loads, however, time-triggered systems excel because they have much less overhead than event-triggered systems.

Time-triggered systems have another advantage. If the machine in our robot example is extended by another rotating part, it is easier to fit the new control software into the system if it is time triggered. The service routine of the new part has a known deadline and duration, and can therefore be added to the system without changing the existing service routines. If the system can still be scheduled, no further work has to be done (if it is not, Jbed detects this at load time and the system will not start up). Event-triggered systems, however, will likely require many changes. Time-triggered systems, thus, pave the road for the use of software components (like JavaBeans) in real-time systems.

Java Library for Real Time

Jbed does not make any changes to the Java language. To support real-time embedded systems, Jbed adds library classes -- some of which are built in (to access hardware directly from Java, for instance), while others are regular libraries written in Java. The most important such library is the task library, com.jbed.tasks.

The task library defines a class Task, which is a subclass of java.lang.thread. Real-time tasks in Jbed therefore behave exactly like Java threads, except that they give additional guarantees concerning their real-time behavior. As with Java threads, Task objects control the behavior of a concurrent activity; they contain all the bookkeeping information necessary to do this. You implement the activity itself by writing a class that implements the interface java.lang.Runnable -- again, just as with regular Java. The run method of this interface contains code that makes up the concurrent activity. As in Java, an alternative to this approach is to subclass the Task class and implement user code in the run method of the class. In typical nonreal-time programs, a thread's run method contains an endless loop. This is different for real-time tasks: A Task should be restarted by Jbed so that it can automatically start the next iteration at the right time.

The Jbed scheduler uses timeslices to assign computation time to tasks. A timeslice is an implementation-dependent short period of time; one millisecond, for instance. The implementation's timeslice is provided in the static variable com.jbed.tasks.Task.timeslice; see Listing One. The scheduler installs a timer interrupt to get control (after a timeslice has passed) by preempting the currently running task. The scheduler then checks whether the previously running task has exceeded its admitted duration; if so, it is aborted. The scheduler determines which task has the closest deadline to meet, and lets it run for the next timeslice. Rescheduling may occur because of preemption after a timeslice, and also voluntarily when the running task stops or blocks (for instance, by sleeping or waiting for a signal).

Jbed distinguishes between the six types of real-time tasks in Table 1. The type of Task is given in the constructor using the RealtimeEvent argument.

When a task is created, its constructor initializes it if the scheduler's admission testing was successful. This is the case if the scheduler can provide sufficient computation time to the new task; that is, if other tasks have not yet reserved too much of the processor's capacity. Otherwise, a com.jbed.tasks.AdmissionFailure exception is thrown. Listing Two is a typical example of how a task is created. The first statement creates an instance of one of the real-time event classes. Depending on the chosen event class, the real-time behavior of the task is different. Depending on the event class, you may need to use a try/catch statement for creating the event object. The second statement creates an instance of a runnable object (an instance of a class implementing the java.lang.Runnable interface). This object implements your application-specific behavior. The try/catch statements create the task object, using the event object and the runnable object as parameters, plus the duration, allowance, and deadline values specified in microseconds. The last statement starts the task, which means that it is marked as ready to run. The scheduler then executes the task at the appropriate time.

Two arguments to the Task constructor, duration and deadline, have been explained already. The additional argument allowance requires further explanation: It is the time required for the handler of the DurationOverflow exception that is thrown if the task exceeds its duration. Without an allowance for this, the system might not have any time left to schedule it. Frequently, this situation is simply an error condition that requires no handling, and is removed during the debugging phase. Then the allowance can be set to zero. The task's constructor throws an IllegalArgumentException exception unless the following holds:

duration > 0

allowance >= 0

deadline > duration + allowance

If a system contains both Java threads and real-time tasks, Jbed schedules the threads whenever no task is running according to its priority (round-robin if several have the same priority). An example of such a thread is the garbage collector.

The Robot Example

Recall that the goal in our real-time robot example is to move two orthogonal robot axles such that their combined movement follows a Lissajous figure with given parameters. The example has three real-time tasks and no user-defined Java threads. One of the three tasks, the WatchDog, is simply used to check if the software is alive and running. If it is not, the hardware shuts down. The other two are the Planner task and Control task.

To reduce the memory footprint, the example avoids repeated dynamic memory allocation and the target bytecode compiler (TBCC) is not used. Jbed is modular, and unused modules (such as the garbage collection or the TBCC in this case) are not loaded. Dynamic memory allocation is avoided by using static variables. Listing Three presents the definitions of some of these variables and definitions (the complete listings are available electronically; see "Resource Center," page 5).

The Planner task simply calculates the desired position of the two axles from the given parameters at the current time and then advances time. The sine method (from Jbed's math library com.jbed.math .FMath) is used instead of the Java math library, which is far too large for embedded systems; see Listing Four.

The Control task has the more complex job of comparing the actual and the desired positions and calculating a reasonable movement for the current timeslice. The classes com.jbed.peripherals.Counter and com.jbed.peripherals.AnalogActuator interface to the actual hardware and allow mapping of, for example, an analog input reading from hardware to a given floating-point range. In our robot example, a mapping to an angle in radians was chosen in the initialization; see Listing Five.

So far we have defined what the real-time tasks do. Now we need to set them up such that they run in real time within reasonable time constraints. Since this robot example is time triggered, the tasks are to run at regular intervals and poll the hardware. First, a PeriodicTimer event is set up that fires every 5 milliseconds. Then a periodic Task is created based on that. The Task has a duration of 2 milliseconds -- no allowance for an error handler and a deadline, which is the same as the period; see Listing Six.

Because the Planner does trigonometric calculations, it runs at twice the period of the Control task, namely 10 milliseconds. To do this cleanly, Jbed offers a special type of task called "harmonic" task. The corresponding RealtimeEvent is a HarmonicEvent that triggers at every second start of the Controller. With these six statements, you have set up the entire real-time system.

The ADAPT Project

The Advanced Diagnosis with the Applet Technology (ADAPT) project is a European Union-supported collaboration between Mecel (a Swedish company that develops electronics and software for the automotive industry; http://www.mecel.se/) and Trialog (a French software house that focuses on embedded systems and home/vehicle connectivity solutions; http://www.trialog.com/) that aims to develop methods for remote downloading of software to cars.

As cars become more and more complex, much of their functionality is controlled by electronic control units (ECUs). These ECUs are connected and communicate with each other in networks. When an error occurs somewhere in the network, the symptom can occur at a totally different place. Thus, it is hard to localize faults in new cars and that is why a good diagnostics system has become more and more important. The main point is to be able to download and run Java-based diagnosis components (Diaglets) via, for example, a diagnostic web site.

To enable this, at least one of the ECUs in the car has to support code downloading. In the ADAPT project, a telematics unit (TU), with GSM modem, is used inside the car as a communication link between the diagnostics web site and car. The TU uses Jbed as the operating system, which has a built-in Java virtual machine (JVM), and code can dynamically be loaded and unloaded via GSM. It will also be possible to download diaglets from the TU to other ECUs inside the car if those ECUs have JVMs. In the ADAPT project, all parts of a complete system for remote car diagnostics are taken care of -- programs for the diagnostics web site, all embedded programming inside the cars, security and safety aspects, and so on. To simplify development, all programming is in Java.

Device Drivers in Java

One of the most fundamental qualities of the ADAPT system is dynamic downloading of code, which is a feature that the Java language in itself supports. That was one of the main reasons why we chose Java in the first place. After we decided to use Java, our next decision was whether to write the device drivers in C and use a native interface between C and Java, or write them in Java. C is often seen as the natural choice for drivers because it allows tight control of the hardware, but Java drivers have some advantages. The most obvious is that you have one consistent code base that can be manipulated with one set of tools, instead of having two different sets of code with two different sets of tools. There is also no need to have any awkward half-Java/half-C interfaces between the two.

Java has some other advantages compared to C. For example, javadoc is a built-in documentation system that produces documentation in HTML format. Of course, tools like this exist for C, but it is a standard tool in Java and you can count on it being there.

Also, Java produces cleaner code and less room for errors by having features such as strong typechecking, garbage collection, exceptions, and no pointer arithmetic.

Because of the safety checks Java makes during run time, Java is slower than C, even when compiled (of course, this depends on your compiler). Listing Seven shows some of the assembly code generated by the compiler for the innermost loop in one of our communication-bus-driver interrupts. This code is performance critical since failure to execute in time leads to loss of data. In Listing Seven, the extra lines of code added for safety checks are highlighted. These add 20-25 percent to the execution time. But the safety checks increase stability and reduce debug time, so they are a definite advantage for all but the most speed-critical code. In Jbed, you can turn off run-time checks on a per function basis. The possibility to turn them off in performance-critical parts of the code gives us the best of both worlds.

Another tricky area when writing device drivers involves interrupts. Jbed can handle interrupts in two different ways. The old-fashioned way has a pointer to the interrupt-handler function, which executes immediately and bypasses the normal scheduling of threads and tasks. (Jbed differentiates between normal Java threads, which execute with no real-time requirements, and real-time tasks that are guaranteed to execute within a deadline.) In the other scenario, Jbed can schedule the interrupt as a real-time task that cooperates with the other threads and tasks. Tasks, however, cannot be scheduled with greater granularity than the minimum timeslice -- in our case, a 22-MHz 68376, which was set to 1 millisecond. Because the interrupts from the CAN bus could come as often as every 200 µs (at 500 Kbaud), we were forced to use the traditional approach.

Having interrupt handlers preempting the normal threads and tasks creates another set of problems:

  • You cannot expect the tasks to deliver on any real-time demands when they can be interrupted at any moment and for an arbitrary amount of time.
  • All Java functions that trigger a rescheduling (such as wait and notify) cannot be called from the interrupt handler. Calling any such function would make the scheduler continue executing a thread or task and you would never be returned to the interrupt handler, which would leave your stack a permanent mess.

  • You cannot allow any exceptions to be thrown in the interrupt handler, as this would also result in a messed-up stack. This problem is another reason you would turn off the safety checks. No safety checks means no stack-messing interrupts.

In ADAPT, we turn off the safety checks only for the most performance-critical parts of our drivers and the interrupt handlers.

When writing device drivers, you must have read/write access to memory. Normally, Java does not support direct memory access, but some kind of support for that is obviously needed. The Jbed operating system supplies you with some built-in functions for memory access. To increase the safety of those functions, we encapsulated them once more by creating a set of classes that represents bits, bit-fields, and fields at byte-sized and short-sized memory locations. Listing Eight is one of those classes. It could, for example, be used to represent a value consisting of the bits 4 to 6 (a mask value of 0x70) in a byte-sized hardware register. Using classes like this gives a reasonable level of safety to an otherwise completely unsafe write operation directly to memory. To achieve this and to keep the performance overhead low, as much checking as possible is done in the constructor, which is only called once. When the memory-write function executes, only a simple bounds check is performed. And because we can be sure that the variables in the functions are correct, you can turn off the built-in safety checks for an even smaller performance hit. And with Java exceptions, we can, in many cases, gracefully recover from situations that could otherwise lead to a system crash.


Listing One

public class Task extends java.lang.Thread {
    public static final int timeslice;
    public Task (java.lang.Runnable target, long duration,long allowance,
        long deadline, RealtimeEvent event) throws AdmissionFailure;

Back to Article

Listing Two

// some real-time constructor is called:
com.jbed.tasks.RealtimeEvent event = new com.jbed.tasks....(...);
// some object implementing the java.lang.Runnable interface is created:
java.lang.Runnable target = ... ;
com.jbed.tasks.Task task;
try {
    task = new com.jbed.tasks.Task(target, duration,
        allowance, deadline, event);
catch (com.jbed.tasks.AdmissionFailure e) {

Back to Article

Listing Three

public class LPointer {

    // the two clocks in ms:
    static final int ControllerClock = 5;
    static final int PlannerClock = 10;

    // the calculation parameters:
    static final float Kp0 = 500.0F, Kp1 = 600.0F;
    static final float Kd0 = 35.0F, Kd1 = 10.0F;

    // the Lissajou parameters:
    static float    a0 = 0.05F, a1 = 0.05F;
    static float    f0 = 12.56F, f1 = 6.28F;
    static float    fv0 = 0.0F, fv1 = 0.0F;

    // actual positions and previous positions:
    static float q0actual, q1actual, q0old , q1old;

    // the three tasks:
    static com.jbed.tasks.Task  controllerTask, plannerTask, watchdogTask;

    // the periphery (actuators and counters):
    static com.jbed.peripherals.AnalogActuator out0, out1;
    static com.jbed.peripherals.Counter counter0, counter1;

    // the desired positions:
    static float q0desired, q1desired;

    // the time:
    static float t=0;


    static void init() {
       try {
          // Configuration
          float gear = -100F;
          float nmPerAmp = 0.05F;
          float maxAmp = 6.0F;
          float maxNm = maxAmp * nmPerAmp * gear;
          float scale = 2 * maxNm / 4096;

          // define the scale for the actuators:
                                                0, 4095, scale, maxNm);
          drivers.demo.DAC.defModule("DACMod", 0x0FFFF8100);
          drivers.demo.DAC.defChannel("A0", "DACMod", "DAScale", 3);
          drivers.demo.DAC.defChannel("A1", "DACMod", "DAScale", 4);

          // define the scale for the input counters:
          float counterConst = 2000; // = 4 * 500
          scale = 2 * com.jbed.math.FMath.PI / (counterConst * gear);
              "EncScale", Integer.MIN_VALUE, Integer.MAX_VALUE, scale, 0F);
          drivers.demo.Enc6.defModule("Enc6Mod", 0x0FFFF8300);
          drivers.demo.Enc6.defChannel("Counter0", "Enc6Mod", "EncScale", 3);
          drivers.demo.Enc6.defChannel("Counter1", "Enc6Mod", "EncScale", 4);

          // Initialization
          out0 = new com.jbed.peripherals.AnalogActuator("A0");
          out1 = new com.jbed.peripherals.AnalogActuator("A1");
          counter0 = new com.jbed.peripherals.Counter("Counter0");
          counter1 = new com.jbed.peripherals.Counter("Counter1");
      } catch (Exception e) {
            // ...

Back to Article

Listing Four

static class Planner extends com.jbed.tasks.Task {
    public Planner(long duration, long allowance, long deadline,
                        jbed.tasks.RealtimeEvent event) 
        throws com.jbed.tasks.AdmissionFailure  {
        super (duration, allowance, deadline, event); 
    public final void run() {
        q0desired = (a0*com.jbed.math.FMath.sin(f0*t+fv0));
        q1desired = (a1*com.jbed.math.FMath.sin(f1*t+fv1));
        t = t+(float)(PlannerClock/1000.0F);

Back to Article

Listing Five

static class Control extends com.jbed.tasks.Task {
    public Control(long duration, long allowance, long deadline,
                        jbed.tasks.RealtimeEvent event)
                        throws com.jbed.tasks.AdmissionFailure  {
        super (duration, allowance, deadline, event); 
    public final void run() {
        float tau0, tau1;
        float dpos0, dpos1;
        q0actual = counter0.read();
        q1actual = counter1.read(); 
        dpos0 = q0desired - q0actual;
        dpos1 = q1desired - q1actual; 
        tau0 = Kp0*dpos0-Kd0*
        tau1 = Kp1*dpos1-Kd1*
        out0.write(tau0);  out1.write(tau1);
        q0old = q0actual; q1old = q1actual;

Back to Article

Listing Six

com.jbed.tasks.RealtimeEvent periodicEvent =
    new com.jbed.tasks.PeriodicTimer(ControllerClock*1000);
controllerTask = new Control(2000, 0, ControllerClock*1000, periodicEvent);

com.jbed.tasks.RealtimeEvent harmonicEvent =
    new com.jbed.tasks.HarmonicEvent(controllerTask, 2);
plannerTask = new Planner(1000, 0, 10000, harmonicEvent);

Back to Article

Listing Seven

          MOVEA.L   (-16, A6), A0
001F0F6A  TST.L     A0
<b>001F0F6C  TRAPEQ</b>
001F0F6E  MOVEA.L   (12, A0), A0
001F0F72  MOVE.L    (-8, A6), D1
001F0F76  ADD.L     (-20, A6), D1
001F0F7A  MOVEA.L   D1, A1
001F0F7C  MOVE.L    (-20, A6), D0
<b>001F0F80  TST.L     A0</b>
<b>001F0F82  TRAPEQ    
001F0F84  CMP.L     (12, A0), D0
001F0F88  TRAPCC</b>
001F0F8A  MOVE.B    (-3834, A1), (16, A0, D0.L*1)
001F0F90  ADDQ.L    #1, (-20, A6)
001F0F94  CMPI.L    #$00000008, (-20, A6)
001F0F9C  BLT       $001F0F66

Back to Article

Listing Eight

public class ByteRegisterField
    private int m_mask;
    private int m_base;
    private int m_pos=0;

    private int m_maxval;
    private static IllegalArgumentException IllegalArgument = 
                                        new IllegalArgumentException();
    public ByteRegisterField(int base_adress, int mask)
        //check to see that the mask is in the register and nonzero
        if (m_mask>0xFF||m_mask<=0)
            throw IllegalArgument;
        //Find the lowest bit in the mask
        int i=1;
        //check to see if the mask is without holes;
            throw IllegalArgument;                  
    public void Set(int value)
        //instruct the compiler to turn of safety checks
          //check that the value is valid
          if ((value > m_maxval) ||value <0)
               throw IllegalArgument;
          Unsafe.getByte(m_base) & ~m_mask | (value << m_pos))); 
    public int Get()
      //instruct the compiler to turn of safety checks
      com.jbed.runtime.Unsafe.option(com.jbed.runtime.Unsafe.noChecks );
      return (com.jbed.runtime.Unsafe.getByte(m_base) & m_mask)>>>m_pos;

Back to Article

Related Reading

More Insights

Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.