Blogs PTC Perc Frequently Asked Questions

PTC Perc Frequently Asked Questions

May 8, 2025
Randy Rorden is a Software Development Director at PTC, managing the development and support of the Perc Real-Time Java product for over 25 years. He has extensive experience in Linux development, Java garbage collection, real-time scheduling, networking, and encryption. He holds patents in computer memory architecture and high-speed digital communications. Randy has a bachelor’s degree in electrical engineering from Brigham Young University.
See All From This Author

I don’t believe you. How can Java do real time?

Your disbelief is not surprising. It can be hard to imagine something that is counter to everything you’ve been told about Java all your life. Daniel Kahneman in his book “Thinking Fast and Slow” mentions the tendency to make judgements and decisions based solely on information readily available, while ignoring or underestimating missing or incomplete information. He called it “What You See Is All There Is” (WYSIATI).

Let’s challenge that bias with some facts:

  • Java’s specifications don’t preclude real-time execution. There is nothing inherent in the Java Language Specification or Java Virtual Machine Specification that precludes real-time execution.
  • Traditional JVMs have made strides in reducing non-determinism. For example, Oracle and OpenJDK JVMs (let’s call them “trad-JVMs”) have spent years improving garbage collection (GC), a major source of non-determinism. Java 17 offers four GC algorithms, each with trade-offs between performance and latency. Azul Systems even offers a “pauseless” GC in their enterprise-class Zing JVM.
  • The real challenge lies in operating system support. Many operating systems don’t support real-time execution, and making JVMs fully real-time would require architectural changes that could alienate customers. That’s why trad-JVMs offer multiple GC algorithms—some users prioritize raw performance, while others value shorter pauses.
  • Perc is built for deterministic behavior. Perc’s GC can be preempted at any time by high-priority Java threads and will resume seamlessly. It uses parallel worker threads running concurrently with Java threads, leveraging multi-core processors to maintain performance without sacrificing determinism.

But there is much more to supporting real time than the garbage collector.

  • JIT Compiler Interruptions:
    Trad-JVMs have unpredictable Just-in-Time (JIT) compiler interruptions. Unless you disable the JIT compiler, they will arbitrarily pause a thread to compile interpreted bytecode to native code or optimize previously compiled code. Perc gives you the option to bypass the JIT compiler by offering Ahead-of-Time (AOT) compilation. With Perc, bytecode can be compiled at build time rather than at run time.
  • Thread Scheduling:
    Trad-JVMs simply hand threads over to the Operating System and let it determine when to run them. Even though Java threads are assigned a priority, there is no way to predict when or in what order they will run. The OS has its own scheduling algorithms and may, for example, try to “fairly” allocate CPU time based on threads’ previous CPU usage. That violates real-time design principles in which thread priority must reign supreme. Perc has its own thread dispatcher using Linux real-time scheduling policies and CPU core affinity to guarantee the N highest priority ready threads run on N available cores. Always. The Perc dispatcher also implements Priority Inheritance protocol to prevent priority inversions among Java threads. Trad-JVMs don’t do any of that. 

The next time someone tries to tell you that Java can’t do real time, send them our way. We are always happy to expand people’s understanding beyond WYSIATI.

Why not use C/C++ or Rust?

Compared to C/C++, Perc uses the Java language, which has:

  • Automatic garbage collection, freeing the programmer from the burden of memory management
  • Memory safety
  • Type safety enforced at compile time and runtime (cast checking)
  • Exception handling
  • Built-in thread management
  • Thread-safety at the language level (synchronized blocks)
  • Higher developer productivity resulting from:
    • All of the above (i.e. less time spent debugging)
    • An ecosystem of built-in and third-party libraries

Why Java (and Perc) outshines Rust:

Rust offers memory safety but has its own challenges:

  • No garbage collector: Rust has the memory safety that C lacks but has its own disadvantages for building large systems. It has no garbage collector and relies upon object ownership to free memory that is no longer in use. There can be only one owner of any object at a time, and when it goes out of scope, the object will be freed, but the memory heap will never be defragmented. A system intended to run for long periods of time may find that memory is fragmented to the extent that large allocations are no longer possible. Rust developers suggest using memory pools or allocating large objects first and keeping them owned for the life of the program. Perc’s garbage collector automatically compacts objects in heap memory without pausing the application.
  • Complex ownership rules: To enforce single ownership, the Rust compiler has rather complicated rules that can be frustrating for the developer. It allows “borrowing” of objects (using the & character, analogous to C++ references) but a borrowed reference does not allow the borrower to modify the object unless it is made “mut[able]”. And a single mutable reference cannot co-exist with any other (even non-mutable) references.
  • Smaller ecosystem: Though it is expected to grow over time, Rust’s ecosystem of libraries (“crates”) is currently small and mostly from third parties (which entails a risk of malware). Java’s ecosystem is huge, having 25 years of development behind it, and those libraries that are part of the Java distribution are well-vetted for security vulnerabilities.

For a small application that is not expected to be widely distributed, allocate/deallocate objects over long periods, or to change much in the future, these disadvantages may not be significant; for a larger system or one that is expected to be enhanced over the long term, they may make it unmaintainable.

Additionally, Perc also includes features to harden your application against hackers, which may be important to protect intellectual property or in security-sensitive environments.

Do I have to write special code to run on Perc?

No. Perc is a Java Standard Edition JVM. It runs standard Java SE code like trad-JVMs. With that said, there are some coding practices that will improve deterministic behavior in Perc. We have a Technical Note describing them in greater detail:

  • Set thread priorities based on the relative criticality of each thread. Don’t just leave them at the default Thread.NORMAL priority (5). Higher priorities should be assigned to truly time-critical functions. Use priorities above the GC priority (default 6) for threads that need to preempt the GC.
  • Avoid timing “jitter” in the standard java.util.Timer class by using a Perc-specific Timer API supporting expirations specified in absolute time rather than relative time.
  • Avoid spin loops: Replace Thread.yield() with Object.wait/notify or a Semaphore.
  • Try to reuse Java objects rather than creating and discarding them at excessive rates. Use pools of pre-allocated objects to process streams of data between application threads. Similarly, use Thread Pools to process work flows concurrently rather than creating and discarding Threads.

Do I have to ROMize all my Java classes and resources?

No. You can run your Java application with a default configuration of the Perc VM with dynamic class loading and JIT compilation enabled just like a trad-JVM. In fact, we recommend you start your migration from a trad-JVM to Perc with this approach.

  • You can use tools like the Perc Shell to monitor CPU and memory resource usage.
  • Then, for better real-time latencies, you can follow our Technical Note to set Perc VM options for timing and real-time policies and priorities.
  • Only then do we recommend using the ROMizer to package and AOT-compile classes and resources into a customized Perc VM binary.

We have a Technical Note for that as well.

How do I package Files into a Perc VM binary?

In addition to packaging Java classes and resources into a Perc VM binary, the ROMizer can package Virtual File Systems (VFS) containing directories and static files into a PVM binary. The files are extracted as objects in heap memory and accessible via the standard java.io and java.nio libraries at run time. VFS files are initialized from a ROMized zip archive at startup and while writing is permitted, data written by the application will not persist across Perc VM restarts.

For more details, refer to our Technical Note on using Virtual File Systems with Perc.

Randy Rorden Randy Rorden is a Software Development Director at PTC, managing the development and support of the Perc Real-Time Java product for over 25 years. He has extensive experience in Linux development, Java garbage collection, real-time scheduling, networking, and encryption. He holds patents in computer memory architecture and high-speed digital communications. Randy has a bachelor’s degree in electrical engineering from Brigham Young University.

Up Next