The Java Native Interface (JNI)
Java provides a framework called the Java Native Interface (JNI), with which
it is possible to write native methods. A native method is a method that is mapped
to a function in a library of native code, such as a DLL under Windows. Or, to put things
more simply, you can write code in C or C++1 and call it from Java.
When do you need to use a native method?
The usual reasons for writing a native method are:
- to access facilities of the operating
system not provided via the standard Java libraries;
- you have some library or code already written in C/C++ that you want
to interface with without porting it to Java;
- in a few rare cases where there is a proven performance benefit
(e.g. because you know your particular C compiler can optimise some particular code
better than the JVM's JIT compiler).
The benefits of using native code may need to be weighed up against the resulting
loss in portability, security and codability.
Caveat: beware false performance assumptions
If you are porting code from Java to C purely because of a believed
performance gain, it is worth considering whether the gain is actually
real and whether it is really attributable to using native code. The JIT compilers
built into modern JVMs such as Hotspot already perform a large number of
optimisations, for example:
- modern JIT compilers can perform various optimisations such as loop unrolling,
escape analysis and "architecture savvy" optimisations
(e.g. re-ordering instructions to take advantage of parallelism
built into a particular CPU) just like regular compilers;
- accesses to Java object fields can compile to load/store machine instructions;
- basic maths operations often compile directly to floating point machine instructions;
- operations on ByteBuffers generally
compile directly to load/store machine instructions.
Some cases where native code is apparently "faster" than its Java counterpart
turn out to be false comparisons. For example, if you have a List of
Java objects which you then code in C as an array of structs, the C version
does away with some overhead from object housekeeping and garbage collection. However,
this optimisation isn't tied to the language per se:
you could also do away with that overhead in Java by storing all the data records one
after the other in a big ByteBuffer (perhaps the nearest equivalent to an array of structs).
Later on in this section, we look in more detail at typical
JNI call overhead.
Requirements for writing native methods
Having carefully the pros and cons, if you are still convinced that you need to
write native methods, then the essential requirement is a C compiler than can produce
a dynamic library on your particular platform (e.g. a DLL in Windows) and which can
compile against the libraries that you need to use. Possible choices
include Eclipse, Visual Studio under Windows, or for diehards,
running gcc/cc from the command line.
In this tutorial, we include a few practical examples from Visual Studio. However,
the general principles we illustrate apply to other compilers.
Next: getting started with JNI
On the next page, we look at how to get started with JNI
and the stages involved in setting up a DLL/dynamic library project and writing our
first native method.
1. C or C++ are the most popular choice for writing native methods
to be called from Java, and the JDK provides a tool to help with writing them in C/C++.
But in principle, you should be able to write native methods in any language that you can
compile into a dynamic library on your particular platform.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.