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:
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)
All the native methods declared in the Runtime.java file can be found in the corresponding Runtime.class file, located at /usr/lib/jvm/default-java/jmods/java.base.jmod/classes/java/lang/Runtime.class for Ubuntu Linux for example, as shown bellow:
As a comparison, the method 18 runFinalization is a non-native Java method, and it contains the 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

Popular posts from this blog

What is Java Synthetic Class (ACC_SYNTHETIC)? with the example of BoundMethodHandle$1.class in Java package java.lang.invoke

What is Java Bridge Method (ACC_BRIDGE)?