Part 48 - native method calls explain the executed Java methods

Posted by tryin_to_learn on Thu, 30 Dec 2021 09:32:24 +0100

Take an example of a Java method executed by a native method call, as follows:

public class TestJNI {
    static {
        System.load("/media/mazhi/sourcecode/workspace/projectjava/projectjava01/src/main/java/libdiaoyong.so"); 
    }
    
    public static int getResult() {
    	return 2;
    }
  
    public static native int get();
  
    public static void main(String[] args) {
        TestJNI.get();
    }
}

As mentioned above, the native method get() is called in the main() method. The specific calling process has been introduced in detail in the previous article. In this example, we will introduce that the native method get() calls the Java method getResult().

The implementation of the local function of the called native method get() is as follows:

JNIEXPORT jint JNICALL Java_TestJNI_get(JNIEnv * env, jclass jc){
  jclass cls = (*env)->FindClass(env, "TestJNI");  
  jmethodID id = (*env)->GetStaticMethodID(env, jc, "getResult", "()I");
  jint x = (*env)->CallStaticIntMethod(env,cls, id);
  return x; 
} 

In the above function, we will call the getResult() method of TestJNI class to obtain an integer value, and then the local function returns the integer value.

The CallStaticIntMethod() function called is implemented as follows:

extern "C" {
 jint  jni_CallStaticIntMethod(JNIEnv *env, jclass cls, jmethodID methodID, ...) {
    JavaThread* thread=JavaThread::thread_from_jni_environment(env);
    ThreadInVMfromNative __tiv(thread);

    HandleMarkCleaner __hm(thread);
    Thread* __the_thread__ = thread;
    os::verify_stack_alignment();
    WeakPreserveExceptionMark __wem(thread);
    jint ret = 0;

    va_list args;
    __builtin_va_start(args,methodID);
    JavaValue jvalue(T_INT);
    JNI_ArgumentPusherVaArg ap(methodID, args);
    jni_invoke_static(env, &jvalue, 0, JNI_STATIC, methodID, &ap, __the_thread__); 

    // ...

    __builtin_va_end(args);
    ret = jvalue.get_jint();
    return ret;
 } 
}

JNI called_ invoke_ The static() function is implemented as follows:

// Calling Java static methods through JNI
static void jni_invoke_static(
 JNIEnv              *env,
 JavaValue*          result,
 jobject             receiver,
 JNICallType         call_type,
 jmethodID           method_id,
 JNI_ArgumentPusher  *args,
 TRAPS
){
  Method* m = Method::resolve_jmethod_id(method_id);
  methodHandle method(THREAD, m);

  ResourceMark rm(THREAD);
  int number_of_parameters = method->size_of_parameters();
  // Here, the parameters to be passed to the Java method are converted into JavaCallArguments instances and passed down
  JavaCallArguments  java_args(number_of_parameters);
  args->set_java_argument_object(&java_args);

  // Fill in the JavaCallArguments instance with the method fingerprint value
  Fingerprinter fp =  Fingerprinter(method);
  uint64_t x =  fp.fingerprint();
  args->iterate(x);
  // Initialization method return type
  BasicType bt = args->get_ret_type();
  result->set_type(bt);

  // Call java method
  JavaCalls::call(result, method, &java_args, CHECK);

  // When the Java method returns object type data, it needs to be handled and stored in the result
  if (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY) {
     oop tmp =  (oop) result->get_jobject();
     jobject jobj = JNIHandles::make_local(env,tmp);
     result->set_jobject(jobj);
  }
}

The called JavaCalls::call() function will eventually be called to JavaCalls::call_helper() function, which was described in detail when explaining the execution of Java methods. When calling the Java method getResult(), the state of the stack is shown in the following figure.

The blue is the stack frame of C/C + + functions, and the green is the stack frame of Java methods.  

Now we will call the native method get () from the main () method that explains the execution, which was described in detail in the previous article, and from Java_ TestJNI_ The process of the get() function calling getResult() is very similar to the process of the HotSpot VM calling the main() method. The process of the HotSpot VM calling the main() method has been described in detail before and will not be described in detail here. Here we introduce the JavaCallWrapper class.

From the above figure, we can see that the Java stack and C/C + + stack are mixed together, which adds a lot of difficulty to the expansion of different types of stacks (for example, GC needs to traverse stack frames, exceptions need to look up exception handlers, etc.), and the conversion and adaptation of different types of stack frames, which will be described in detail later.

The official account is analyzed in depth. Java virtual machine HotSpot has updated the VM source code to analyze the related articles to 60+. Welcome to the attention. If there are any problems, add WeChat mazhimazh, pull you into the virtual cluster communication.