Java Native Interface compared to Python/C API
Introduction
Java and Python are both high-level object-oriented programming languages. Sometimes they need to invoke code implemented in a lower level language like C++, C, and assembly for the sake of faster calculations, finer control over memory layout, or machine-specific features.
I have worked with the standard foreign function interface (FFI) in both languages – Java’s JNI and CPython’s C API – and found interesting differences in the architecture of their functions and data values. By comparing and contrasting the two languages, I hope to illustrate that JNI and CPython take diametrically opposite approaches in many aspects. Both approaches are appropriate for their respective language environment, but would fare poorly in the other language – neither way is right or wrong in an absolute sense.
Code examples
Here are two complete application examples in both Java and Python. Download all the files in java-python-native-examples.zip, or view individual files below:
Program | Java | Python |
---|---|---|
Sum array | ||
Create map |
The first program, “Sum array”, demonstrates how native C code can read numbers coming from Java/Python code. The comparison is not fair because Java has primitive integer types whereas Python only has bigint objects. The Java version shows how the Java primitive types easily correspond to C integer types, and how arrays of numbers are accessed in JNI. The Python version shows how to convert between Python’s int
objects and native C integer types.
The second program, “Create map”, demonstrates how native C code can create a complex graph of Java or Python objects. The Java version looks up existing Java classes and methods, builds strings in C, then invokes the Java methods to create the necessary objects. The Python version looks up one custom function, but otherwise invokes statically known global C functions to create and manipulate Python objects; note that we need to pay attention to reference counts and handle reference stealing and decrementing properly. In this demonstration, there is a fairly good correspondence between the native code in the JNI and CPython versions, because they both take an object-oriented approach to structuring the data.
The C code for Python only works with the CPython 3 interpreter, not Python 2 or a different interpreter. The C code for Java should work with all compliant JDK and JVM implementations.
Feature comparison
Feature | Java | Python |
---|---|---|
Native library loading | ||
Loading mechanism |
|
|
Module mapping |
|
|
Member mapping |
|
|
Working with functions and data | ||
Unique data types |
|
|
Integer types used |
|
|
Arithmetic operations |
|
|
Accessing native API functionality |
|
|
Native function/method arguments |
|
|
Native function/method return value |
|
|
Calling managed methods/functions |
|
|
Preprocessor macros |
|
|
Object reference management |
|
|
Condensed list of JNI API functions
The entire JNI API has just under 70 unique functions as of Java 8. (This is the number once you merge the raw functions that are simply variations on return type or argument style.) This API is surprisingly compact, and you can reasonably memorize the 10 to 20 most commonly used functions after a bit of practice, and rarely need to refer to the documentation thereafter.
-- Getting handles -- FindClass() DefineClass() GetSuperclass() GetStaticFieldID() GetStaticMethodID() GetFieldID() GetMethodID() FromReflectedField() FromReflectedMethod() ToReflectedField() ToReflectedMethod() -- Methods, fields, objects -- GetStatic<Type>Field() SetStatic<Type>Field() CallStatic<Type>Method<Style>() IsSameObject() IsInstanceOf() IsAssignableFrom() GetObjectClass() AllocObject() NewObject<Style>() CallNonvirtual<Type>Method<Style>() Call<Type>Method<Style>() Get<Type>Field() Set<Type>Field() Note: <Style> is blank, A, or V. Note: <Type> is Boolean, Byte, Short, Char, Int, Long, Float, Double, or Object. -- Object references -- GetObjectRefType() NewGlobalRef() NewWeakGlobalRef() DeleteGlobalRef() DeleteWeakGlobalRef() EnsureLocalCapacity() PushLocalFrame() PopLocalFrame() NewLocalRef() DeleteLocalRef() -- Arrays -- New<Type>Array() GetArrayLength() GetObjectArrayElement() SetObjectArrayElement() Get<Primitive>ArrayElements() Release<Primitive>ArrayElements() Get<Primitive>ArrayRegion() Set<Primitive>ArrayRegion() GetPrimitiveArrayCritical() ReleasePrimitiveArrayCritical() Note: <Primitive> is Boolean, Byte, Short, Char, Int, Long, Float, or Double (but not Object). -- Strings -- GetString<Style>Length() GetString<Style>Chars() NewString<Style>() ReleaseString<Style>Chars() GetString<Style>Region() GetStringCritical() ReleaseStringCritical() Note: <Style> is blank or UTF. -- Exception mechanism -- Throw() ThrowNew() ExceptionCheck() ExceptionOccurred() ExceptionDescribe() ExceptionClear() FatalError() -- Miscellaneous -- GetVersion() GetJavaVM() RegisterNatives() UnregisterNatives() MonitorEnter() MonitorExit() NewDirectByteBuffer() GetDirectBufferAddress() GetDirectBufferCapacity()
Notes:
In the list above, the function names come from the JNI API, but the subheading names and the way the functions are grouped are made by Nayuki (not a part of any official specification).
Working with static methods and fields uses a different API than working with instance methods/fields. This differs from how the java.lang.reflect API treats both cases the same.
All of the JNI string functions are for convenience and not strictly necessary. Their functionalities can still be accomplished tediously using numerous reflection function calls and temporary values.
Partial list of Python/C API functions
The list of Python/C API functions/macros is enormous, and has over 700 entries as of CPython 3.5. Some very low-level or esoteric functions were arbitrarily excluded by me. An explicit list of entries can be found in python-c-api-function-list.txt. A summary of function counts per topic is given below:
5: Reference counting 79: Exception handling 22: Operating system utilities 25: Importing modules 8: Data marshalling support 9: Parsing arguments and building values 6: String conversion and formatting 7: Reflection 18: Codec registry and support functions 41: Object protocol 38: Number protocol 23: Sequence protocol 12: Mapping protocol 2: Iterator protocol 7: Buffer protocol 14: Type objects 25: Integer objects 2: Boolean objects 10: Floating point objects 13: Complex number objects 15: Bytes objects 10: Byte array objects 138: Unicode objects and codecs 21: Tuple objects 17: List objects 24: Dictionary objects 15: Set objects 12: Function objects 11: Instance method objects 5: File objects 27: Module objects 4: Iterator objects 4: Slice objects 7: Weak reference objects 4: Generator objects 2: Coroutine objects 30: DateTime objects 23: Common object structures