Java Native Methods Essentials, from Binary code point of view, with example of java.lang.Runtime
Java Native method provides a mechanism for Java code to call OS native code, either due to functional or performance reasons.
Binary Code is always the best document.
Let's take the an example from OpenJDK of the class java.lang.Rutime to discover the details in binary code manner.
Here is the source code of the Runtime class:
In this class we have the following methods defined as native:
For the native Java methods, the corresponding generated methods in the class file will set the ACC_NATIVE (0x0100) flag, and the there is no bytecode generated in the
The access_flags field is a mask of flags used to denote access permission to and properties of an method:
The Java part of these methods do not contain any actual logic; the actual logic is in the corresponding C/C++ coding part. We can use the javac -h command (javah command in Java 8 or earlier) to generate the native header file for C/C++, then we can implement the actual coding logic on the native platform, like Windows, Linux, MacOS, etc.
For the java.lang.Runtime.class file in the example above, the corresponding C/C++ native coding logic is located at:
Here are the snapshot of the coding logic in C programming language for native platform as this article is written. We can find that:
In OpenJDK, the Runtime.c file are compiled to the libjava.so file, located at
Note that the java.base.jmod file is in fact a zip file, we can extract the files inside this file using tools like unzip command on Linux.
We can use the objdump command check the libjava.so file, to validate the native methods denoted via the JNIEXPORT in Runtime.c file:
And we can use the GHex tool to open the libjava.so file as binary format, to validate the result shown by objdump above:
So, this is how the Java native method works.
Binary Code is always the best document.
Let's take the an example from OpenJDK of the class java.lang.Rutime to discover the details in binary code manner.
Here is the source code of the Runtime class:
In this class we have the following methods defined as native:
606 public native int availableProcessors();
617 public native long freeMemory();
630 public native long totalMemory();
641 public native long maxMemory();
664 public native void gc();
For the native Java methods, the corresponding generated methods in the class file will set the ACC_NATIVE (0x0100) flag, and the there is no bytecode generated in the
.class
file as other none-native Java methods.The access_flags field is a mask of flags used to denote access permission to and properties of an method:
- access_flags: 257 , which is 0x0101 in hex format, it means to ACC_NATIVE (0x0100) and ACC_PUBLIC (0x0001)
/usr/lib/jvm/default-java/jmods/java.base.jmod/classes/java/lang/Runtime.class
for Ubuntu Linux for example, as shown bellow:- Method 13 availableProcessors: tagged as native and no Code attribute
- Method 14 freeMemory: tagged as native and no Code attribute
- Method 15 totalMemory: tagged as native and no Code attribute
- Method 16 maxMemory: tagged as native and no Code attribute
- Method 17 gc: tagged as native and no Code attribute
Native methods in java.lang.Runtime.class in OpenJDK 11, via Java Class Viewer |
The Java part of these methods do not contain any actual logic; the actual logic is in the corresponding C/C++ coding part. We can use the javac -h command (javah command in Java 8 or earlier) to generate the native header file for C/C++, then we can implement the actual coding logic on the native platform, like Windows, Linux, MacOS, etc.
For the java.lang.Runtime.class file in the example above, the corresponding C/C++ native coding logic is located at:
Here are the snapshot of the coding logic in C programming language for native platform as this article is written. We can find that:
- These C methods are tagged with JNIEXPORT and JNICALL. JNIEXPORT is used to make native functions appear in the dynamic table of the built binary, like *.so file on Unix/Linux like platform or *.dll file on Windows platform. JNICALL contains compiler directives required to ensure that the given function is treated with the proper calling convention when necessary, like __stdcall on Windows platform.
- The method naming convension is Java_package_name_ClassName_MethodName, like Java_java_lang_Runtime_freeMemory, Java_java_lang_Runtime_totalMemory, Java_java_lang_Runtime_maxMemory, Java_java_lang_Runtime_gc, Java_java_lang_Runtime_availableProcessors.
42 #include "java_lang_Runtime.h"
43
44 JNIEXPORT jlong JNICALL
45 Java_java_lang_Runtime_freeMemory(JNIEnv *env, jobject this)
46 {
47 return JVM_FreeMemory();
48 }
49
50 JNIEXPORT jlong JNICALL
51 Java_java_lang_Runtime_totalMemory(JNIEnv *env, jobject this)
52 {
53 return JVM_TotalMemory();
54 }
55
56 JNIEXPORT jlong JNICALL
57 Java_java_lang_Runtime_maxMemory(JNIEnv *env, jobject this)
58 {
59 return JVM_MaxMemory();
60 }
61
62 JNIEXPORT void JNICALL
63 Java_java_lang_Runtime_gc(JNIEnv *env, jobject this)
64 {
65 JVM_GC();
66 }
67
68 JNIEXPORT jint JNICALL
69 Java_java_lang_Runtime_availableProcessors(JNIEnv *env, jobject this)
70 {
71 return JVM_ActiveProcessorCount();
72 }
In OpenJDK, the Runtime.c file are compiled to the libjava.so file, located at
/usr/lib/jvm/default-java/jmods/java.base.jmod/lib/libjava.so
in Unbuntu Linux as example.Note that the java.base.jmod file is in fact a zip file, we can extract the files inside this file using tools like unzip command on Linux.
We can use the objdump command check the libjava.so file, to validate the native methods denoted via the JNIEXPORT in Runtime.c file:
objdump -x libjava.so | grep Java_java_lang_Runtime
objudmp on the libjava.so file, to valiate the JNIEXPORT in Runtime.c file |
And we can use the GHex tool to open the libjava.so file as binary format, to validate the result shown by objdump above:
libjava.so file in binary format, to validate the JNEXPORT defition in Runtime.c, via GHex |
So, this is how the Java native method works.
Comments
Post a Comment