Java Calling Native Code

JNI vers Executing Processes

In a Java environment you sometimes need to call native code written in C++ or C.  You can do this in two ways, using the Java Native Interface JNI/JNA, or by executing native stand alone applications.

When you use JNI, the native code runs in the same process as the calling Java code.

When you execute a stand alone application, the application runs as a separate process.

The advantages of using JNI are:

  • Its potentially faster since there is no process switch.
  • Error handling is easy. You can use exception handling in the JNI code to return Java exceptions.
  • It’s easy to call JNI code since you call the JNI code just like a normal Java method.
  • You can share memory between the JNI code and the Java code (potentially faster processing).

The advantages to executing system processes are:

  • Crash protection, the code is isolated through a process boundary.  If the process crashes, you can catch it in Java and continue.
  • You can run the code outside the Java environment easily and develop it independent of Java.

The disadvantages of using JNI are:

  • A crash in JNI code will bring down the Java VM.
  • It’s hard to setup, you must create binding code between Java and JNI.  (Java Native Access (JNA) makes this easier.)
  • Hard to run JNI code outside of Java.

The disadvantages of executing processes are:

  • Starting a process is hard, you must write platform dependent code in Java to start the process.
  • It’s is clumsy to call, you must pass command line arguments, environment variables. and a starting directory.
  • Loading of shared libraries is a hard platform dependent problem.
  • Error handling is convoluted, you get one int return value, or you must parse standard error or standard out.
  • You cannot share memory between the Java code and the JNI code.

You may argue that you can protect the JNI code using C++ exception handling but this is not the case.  Below is an example crasher that is not caught.

int crasher()
{
    try
    {
        int *ptr = 0;
        *ptr = 1234;
    }
    catch (...)
    {
        // never gets here
        printf("caught exception");
    }
    return 0;
}

Why doesn’t this work? If this was an option, crashing native code could be protected. It seems like an issue with the Java VM.  Here is a note I found under the JNA documentation:

“Set whether native memory accesses are protected from invalid accesses. This should only be set true when testing or debugging, and should not be considered reliable or robust for applications where JNA native calls are occurring on multiple threads. Protected mode will be automatically set if the system property jna.protected has a value of “true” when the JNA library is first loaded.
If not supported by the underlying platform, this setting will have no effect.”

For the project I was working on the crash protection issue overrode all others.  With our limited testing resources and the complexity of our JNI code, we did not want to take the risk of a crashing bug in the native code bring Java down.  So we switched from JNI to native command line applications.

If you have the resources to test and make your native code stable, JNI is the better approach.  Calling system DLLs with JNA is the best approach for calling stable system features not already in the Java API.  Otherwise consider writing your native code as stand alone command line applications and executing the code in a separate process.