preface

In Java, we often encounter the situation of calling local methods, and many classes in the Java core library also make extensive use of local methods. When using JNI, local functions need to be named according to the agreed format. If you do not want to write a long function name, you need to register the method in the JVM. Here’s how to register local methods with the JVM.

Naming conventions

The JVM has a convention on the local method name, which must be complied with when using JNI, that is, Java_

_method.

For example, a Java class that provides local encryption methods is implemented in the ByteCodeEncryptor dynamic library. So its local corresponding function is called Java_com_seaboat_bytecode_ByteCodeEncryptor_encrypt.

package com.seaboat.bytecode;

public class ByteCodeEncryptor {
  static{
    System.loadLibrary("ByteCodeEncryptor"); 
  }
  
  public native static byte[] encrypt(byte[] text);
  
}
Copy the code

registerNatives

If the naming convention of local functions is cumbersome, you can use the registerNatives method to register local functions, so that you can name functions at will. In addition, functions registered to the JVM with registerNatives will execute more efficiently because function searches are faster.

How to sign up

There are two ways to implement local method registration:

Static blocks in Java

  • Declare one in a Java classregisterNativesStatic method.
  • Define one in native codeJava_<fully qualified class name>_registerNativesFunction.
  • Call before calling any other local functionregisterNativesMethods.

For example, in the Object class, do the following:

private static native void registerNatives();
static {
    registerNatives();
  }

Copy the code

RegisterNatives in the local system will bind the specified local methods to the specified functions, for example, the hashCode and Clone local methods are bound to the JVM_IHashCode and JVM_IHashCode functions.

static JNINativeMethod methods[] = {
    {"hashCode"."()I",                    (void *)&JVM_IHashCode},
    {"clone"."()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}
Copy the code

2. Use JNI_OnLoad

The JNI_OnLoad function is called when the JVM executes the System.loadLibrary method, so you can call the RegisterNatives function in this method to register local functions. In this way, you do not need to declare the RegisterNatives local method in the Java class to register the local method.

static JNINativeMethod methods[] = {
    {"hashCode"."()I",                    (void *)&JVM_IHashCode},
    {"clone"."()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

int JNI_OnLoad(JavaVM* vm, void* reserved)
{
...
if ((*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0])) < 0)
{
    returnJNI_ERR; }... }Copy the code

What does registerNatives do

JNINativeMethod defines the JNINativeMethod structure for declaring methods and functions, as follows: Name represents the Java native method name, Signature represents the signature of the method, and fnPtr represents the function pointer.

typedef struct {
    char *name;
    char *signature;
    void *fnPtr;
} JNINativeMethod;
Copy the code

(*env)->RegisterNatives

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}
Copy the code

This method is declared in a JNINativeInterface_ structure, which contains all JNI interface function declarations. The JVM defines the jni_NativeInterface variable to use, and only the RegisterNatives function declaration is listed here. Other functions are omitted.

struct JNINativeInterface_ { ... jint (JNICALL *RegisterNatives) (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods); . } struct JNINativeInterface_ jni_NativeInterface = { ... jni_RegisterNatives, ... }Copy the code

Check out the JNI_ENTRY and JNI_END macros before seeing the implementation of the jni_RegisterNatives function, which strip out the common elements. JNI_END is simple, just two closing braces.

#define JNI_ENTRY(result_type, header) JNI_ENTRY_NO_PRESERVE(result_type, header) WeakPreserveExceptionMark __wem(thread);

#define JNI_END } }
Copy the code

JNI_ENTRY main logic:

  • Gets the JavaThread pointer object for the current thread of execution.
  • Create a ThreadInVMfromNative object.
  • TRACE_CALL, there’s nothing going on here.
  • Create a HandleMarkCleaner object.
  • Assign Thread to Thread in Exceptions.
  • Check stack alignment.
  • Create WeakPreserveExceptionMark object.
#define JNI_ENTRY_NO_PRESERVE(result_type, header) \
extern "C"{ \ result_type JNICALL header { \ JavaThread* thread=JavaThread::thread_from_jni_environment(env); \ assert( ! VerifyJNIEnvThread || (thread == Thread::current()),"JNIEnv is only valid in same thread"); \
    ThreadInVMfromNative __tiv(thread);                              \
    debug_only(VMNativeEntryWrapper __vew;)                          \
    VM_ENTRY_BASE(result_type, header, thread)
    
#define VM_ENTRY_BASE(result_type, header, thread) \
  TRACE_CALL(result_type, header)                                    \
  HandleMarkCleaner __hm(thread);                                    \
  Thread* THREAD = thread;                                           \
  os::verify_stack_alignment();      
Copy the code

Jni_RegisterNatives function is implemented in the following logic:

  • JNIWrapper is used for debugging.
  • HOTSPOT_JNI_REGISTERNATIVES_ENTRY and DT_RETURN_MARK are both used for dtrace.
  • Create the KlassHandle object.
  • Start iterating through the method array to get the corresponding method name, method signature, and method length.
  • Try to see if the corresponding method name and signature already exist in the symbolic constant pool, and throw an exception if not, since Java classes are normally added to the constant pool when they are loaded.
  • callregister_nativeFunction registration.
JNI_ENTRY(jint, jni_RegisterNatives(JNIEnv *env, jclass clazz,
                                    const JNINativeMethod *methods,
                                    jint nMethods))
  JNIWrapper("RegisterNatives");
  HOTSPOT_JNI_REGISTERNATIVES_ENTRY(env, clazz, (void *) methods, nMethods);
  jint ret = 0;
  DT_RETURN_MARK(RegisterNatives, jint, (const jint&)ret);

  KlassHandle h_k(thread, java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)));

  for (int index = 0; index < nMethods; index++) {
    const char* meth_name = methods[index].name;
    const char* meth_sig = methods[index].signature;
    int meth_name_len = (int)strlen(meth_name);

    TempNewSymbol  name = SymbolTable::probe(meth_name, meth_name_len);
    TempNewSymbol  signature = SymbolTable::probe(meth_sig, (int)strlen(meth_sig));

    if (name == NULL || signature == NULL) {
      ResourceMark rm;
      stringStream st;
      st.print("Method %s.%s%s not found", h_k()->external_name(), meth_name, meth_sig);
      THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), -1);
    }

    bool res = register_native(h_k, name, signature,
                               (address) methods[index].fnPtr, THREAD);
    if(! res) { ret = -1;break; }}return ret;
JNI_END
Copy the code

The register_native function has the following logic:

  • Finds the specified method in the corresponding Klass object and throws an exception if it does not exist.
  • If a method is not declared as native, it first tries to find the prefixed native method. This is because it is possible to set the prefix of some native methods in the JVM TI Agent. If it is still empty, an exception will eventually be thrown.
  • Call the most importantset_native_functionFunction to bind C++ functions to the Method object.
  • Is called if the function pointer is nullclear_native_functionClean up the local method object.
static bool register_native(KlassHandle k, Symbol* name, Symbol* signature, address entry, TRAPS) {
  Method* method = k()->lookup_method(name, signature);
  if (method == NULL) {
    ResourceMark rm;
    stringStream st;
    st.print("Method %s name or signature does not match",
             Method::name_and_sig_as_C_string(k(), name, signature));
    THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false);
  }
  if(! method->is_native()) { method = find_prefixed_native(k, name, signature, THREAD);if (method == NULL) {
      ResourceMark rm;
      stringStream st;
      st.print("Method %s is not declared as native",
               Method::name_and_sig_as_C_string(k(), name, signature));
      THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false); }}if(entry ! = NULL) { method->set_native_function(entry, Method::native_bind_event_is_interesting); }else {
    method->clear_native_function();
  }
  if (PrintJNIResolving) {
    ResourceMark rm(THREAD);
    tty->print_cr("[Registering JNI native method %s.%s]",
      method->method_holder()->external_name(),
      method->name()->as_C_string());
  }
  return true;
}
Copy the code

The set_native_function function logic is:

  • throughnative_function_addrThe function gets the address of the local function, which is directreturn (address*) (this+1);As you can see, it directly takes the address of the Method object +1 as the local function address. This is possible because when creating a Method object, it will determine whether it is a native Method. If so, it will leave two additional address locations, one for the local function address and one for the Method signature.
  • Check whether the local function address is already equal to the function pointer, if yes, it is bound, directly return, otherwise continue.
  • Sends events if Jvmti has set propagating bound local method events.
  • Assigns a function pointer to the local function address.
  • GCC gets the compiled function code.
void Method::set_native_function(address function, bool post_event_flag) {
  assert(function! = NULL,"use clear_native_function to unregister natives"); assert(! is_method_handle_intrinsic() ||function == SharedRuntime::native_method_throw_unsatisfied_link_error_entry(), "");
  address* native_function = native_function_addr();

  address current = *native_function;
  if (current == function) return;
  if (post_event_flag && JvmtiExport::should_post_native_method_bind() &&
      function! = NULL) { assert(function! = SharedRuntime::native_method_throw_unsatisfied_link_error_entry(),"post_event_flag mis-match");
    JvmtiExport::post_native_method_bind(this, &function);
  }
  *native_function = function;
  CompiledMethod* nm = code(); 
  if (nm != NULL) {
    nm->make_not_entrant();
  }
}
Copy the code

————- Recommended reading ————

Summary of my open Source projects (Machine & Deep Learning, NLP, Network IO, AIML, mysql protocol, Chatbot)

Why to write “Analysis of Tomcat Kernel Design”

My 2017 article summary – Machine learning

My 2017 article summary – Java and Middleware

My 2017 article summary – Deep learning

My 2017 article summary — JDK source code article

My 2017 article summary – Natural Language Processing

My 2017 Article Round-up — Java Concurrent Article


Talk to me, ask me questions:

Welcome to: