Thread of Java source code registerNatives () in-depth understanding of

Introduction : Read JDK source can be seen, registerNatives () method in the presence of commonly used classes Object class, Class class, ClassLoader class like. Which method is defined as follows:

 private static native void registerNatives();
    static {
        registerNatives();
    }
复制代码

Can get registerNatives () is a native method, which is realized by the C / C ++ realization of the need to thoroughly understand the meaning and role of registerNatives method, you first need to have a thorough understanding of Java in the native keyword (related foundation students can skip directly over this section).

1. Native Method

For explanation native Method keywords official document as follows:

A native method is a Java method whose implementation is provided by non-java code.
复制代码

Native method is a method implemented by non-Java language. Java native method is generally often the C / C ++ implemented, Java API provided with other verbal communication, i.e., JNI (Java Native Interface). If you want to use the Java function calls in other languages, we must follow the JNI API conventions.

1.1 Native usage rules

  1. In addition to native identifier can not be combined with an outer abstract, may be used in combination with other identifiers;
  2. native method java method can return any type, including non-basic types, the abnormality can be controlled;
  3. If the class contains native method method is inherited, the subclass inherits the native method method, you can also use the java language override this method;
  4. If that can not be overridden method is a native method fianl identity, it is inherited.

1.2 Native Method implement step

  1. In Java declaration native()method, and then compile;
  2. Use javah command to generate .h files;
  3. Write file .cpp implement native derivation method wherein the need comprises a .h file generated by the second step (which must contain jni.h note file JDK band);
  4. The native code compiled into a dynamic link library (Windows: * dll, linux / unix: * so, mac os x: * jnilib...);
  5. Java is used System.loadLibrary () method to load the dynamic link library files generated by the fourth step, thus, the entire process is completed.

Here only a brief introduction native knowledge point, if you need more detailed information can access relevant information on their own.

Read the above information shows that registerNatives()the method is implemented by other languages. Can guess by the name of the method registerNatives()is the role of local registration method. Call registerNatives static code block (), it can be drawn to be registered when the class is loaded. I am sure you see where the hearts of the students will certainly appear puzzled why the need to register Native method? registerNatives()How to register the local method?

Here for "The Java Native Interface Programmer's Guide and Specification", give the following explanation fragments.

Before an application executes a native method it goes through a two-step process to load the native library containing the native method implementation and then link to the native method implementation:

1.System.loadLibrary locates and loads the named native library. For example, System.loadLibrary("foo") may cause foo.dll to be loaded on Win32.

2.The virtual machine locates the native method implementation in one of the loaded native libraries. For example, a Foo.g native method call requires locating and linking the native function Java_Foo_g, which may reside in foo.dll.

复制代码

Through the above explanation data shows that before calling a local method, Java will undergo a two-step process to achieve load a local library, the first step is to use System.loadLibrary () method will contain the dynamic files locally implemented loaded into memory; second step when a Java program needs to call the local method, the virtual machine to locate and link to the local dynamic method in the loaded file, thus allowing the implementation of the native method. Now I will explain the meaning of this passage by implementing a native method detailed demo.

2. Use JNI to achieve Native Method

public class ByteCodeEncryptor {
	public native static byte[] encrypt(byte[] text);
	static {
		System.loadLibrary("byteCodeEncryptor.dll");
	}
	public static void main(String args[]) {
		String test = "hello world";
		System.out.println(encrypt(test.getBytes()));
	}
}
分析:以上代码的作用是使用本地方法encrypt()实现加密;
复制代码

Use Javac command to generate more Java source file header files (.h)

#include <jni.h>
#ifndef _Included_com_seaboat_bytecode_ByteCodeEncryptor
#define _Included_com_seaboat_bytecode_ByteCodeEncryptor
#ifdef __cplusplus
extern "C" {
#endif
// 在 Windows 中编译 dll 动态库规定,如果动态库中的函数要被外部调用,需要在函数声明中添加__declspec(dllexport)标识,表示将该函数导出在外部可以调用
JNIEXPORT jbyteArray JNICALL Java_com_individual_thread_register_ByteCodeEncryptor_encrypt
  (JNIEnv *, jclass, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif
复制代码

Achieve encrypt method header file

#include "com_individual_thread_register_ByteCodeEncryptor.h"
#include "jni.h"

void encode(char *str)
{
    unsigned int m = strlen(str);
    for (int i = 0; i < m; i++)
    {
        str[i] = str[i]+4;
    }

}

extern"C" JNIEXPORT jbyteArray JNICALL
Java_com_individual_thread_register_ByteCodeEncryptor_encrypt(JNIEnv *env, jclass cla,jbyteArray text)
{
    char* dst = (char*)env->GetByteArrayElements(text, 0);
    encode(dst);
    env->SetByteArrayRegion(text, 0, strlen(dst), (jbyte *)dst);
    return text;
}
说明:
第一个参数:JNIEnv* 是定义任意native函数的第一个参数(包括调用JNI的RegisterNatives函数注册的函数),指向JVM 函数表的指针,函数表中的每一个入口指向一个JNI 函数,每个函数用于访问JVM中特定的数据结构。
第二个参数:调用Java中native方法的实例或Class对象,如果这个native方法是实例方法,则该参数是 jobject,如果是静态方法,则是 jclass。
第三个参数:Java 对应 JNI 中的数据类型,Java 中 String 类型对应 JNI 的 jstring 类型,(Java于JNI数据类型的映射关系)。
函数返回值类型:夹在 JNIEXPORT 和 JNICALL 宏中间的 jbyteArray,表示函数的返回值类型,对应Java的byte[]类型。
在Java中调用encrypt()本地方法即可得到加密后的结果。
复制代码

Note: to achieve native method requires native C / C ++ source file format method name must be: Java_ full class name _ method name, package name .number to _indicate _method, this is because the JVM has the native method name corresponding provisions required to comply with when using JNI. The method of preparation of the above example to provide a local encryption of Java classes, wherein the method for the local encryption method, implemented in byteCodeEncryptor dynamic library, then its corresponding local function called Java_com_individual_thread_register_ByteCodeEncryptor_encrypt.

3. JNI_onload functions implemented method Native

Use JNI_onload implement Native method, we first need to know JNI_OnLoad function. JNI_OnLoad function which is invoked when performing System.loadLibrary JVM method, the method can be called registerNatives () function local register function. First JNI_OnLoad use steps:

  1. Defining and implementing the corresponding native methods declared in java c / c ++ file, the name of the method may be free, but the number of parameters and parameter types must be the same;
  2. Create a statement JNINativeMethod type of array, which is the local method of dynamically loaded mapping needs.

The following method for local encryption for JNI_onload

3.1 Java Native method declarations

public class RegisterTest {
	static {
		System.loadLibrary("registerTest.dll");
	}
	public native static byte[] encrypt(byte[] text);
	public static void main(String args[]) {
		String test = "hello  world";
		System.out.println(encrypt(test.getBytes()));
	}
}
复制代码

3.2 JNI_onload achieve encrypt method

#include <jni.h>
#include <string>

JNIEXPORT jbyteArray JNICALL encrypt
(JNIEnv *env, jclass cla, jbyteArray text) {
	char* dst = (char*)env->GetByteArrayElements(text, 0);
	unsigned int m = strlen(dst);
	for (int i = 0; i < m; i++)
	{
		dst[i] = dst[i] + 4;
	}
	env->SetByteArrayRegion(text, 0, strlen(dst), (jbyte *)dst);
	return text;
}
/**
第一个参数:encrypt 是java中的方法名称
第二个参数:([B)[B  是java中方法的签名,可以通过javap -s -p 类名.class 查看
第三个参数: (jstring *)encrypt  (返回值类型)映射到native的方法名称
*/
static JNINativeMethod methods[] = {
	{ "encrypt", "([B)[B", (jbyteArray *) encrypt}
};

static jclass myClass;
// 这里是java调用C的存在Native方法的类路径

static const char* const className = "com/individual/thread/register/RegisterTest";

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
	
	JNIEnv* env = NULL; //注册时在JNIEnv中实现的,所以必须首先获取它
	jint result = -1;
	if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { //从JavaVM获取JNIEnv,一般使用1.4的版本
		return -1;
	}
	// 获取映射的java类
	myClass = env->FindClass(className);
	if (myClass == NULL)
	{
		printf("cannot get class:%s\n", className);
		return -1;
	}
	// 通过RegisterNatives方法动态注册登记

	if (env->RegisterNatives(myClass, methods, sizeof(methods) / sizeof(methods[0])) < 0)
	{
		printf("register native method failed!\n");
		return -1;
	}
	return JNI_VERSION_1_4; //必须返回版本,否则加载会失败。
}
复制代码

The same will be packaged into a dll file .cpp file can be called successful in Java, using JNI_onlad achieve native method agree with the general realization native JNI results carefully read the above two implementations can be drawn that distinction, you can use the C JNI_onlad \ when implemented in C ++ native method and its method name can not be head of the same file name generation method, compared with the second approach is more convenient. In addition, the efficiency between them is not the same. Achieve access to relevant information using JNI_onload way native method is more efficient for the following reasons:

  1. The traditional way using JNI, Java classes when calling a local function, usually rely on a virtual machine to a dynamic link library to find a local function (hence the need for naming format specific rules), and use the second approach of the local function method RegisterNatives be registered with the virtual machine, you are allowed to find more efficient function;
  2. Dynamically adjust the mapping between Java native functions and function values ​​at run time, just call RegisterNatives () method multiple times, passing different mapping parameters. Mapping the plurality of native achieved, for example using methods described above static JNINativeMethod methods [] = {{ "encrypt", "([B) [B", (jbyteArray *) encrypt}} manner.

The basic reason can be drawn here Thread, Classload source code using the second approach is faster because registerNatives JNI in () method to make the program initiative will link local method to the caller, when a Java program needs to call the native method can directly calls, without the need to go to the virtual machine locates and links. For a clearer understanding of how to register is required to read the following C / C ++ source code.

4. registerNatives () Code

In Object, Thread class like the native methods specified binding to a specific function by registerNatives, as will bind native methods to clone and hashCode JVM_IHashCode and JVM_IHashCode function.

The following is a part of the code of the code OpenJDK in Thread.c

···
static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
    {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
    {"suspend0",         "()V",        (void *)&JVM_SuspendThread},
    {"resume0",          "()V",        (void *)&JVM_ResumeThread},
    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
    {"yield",            "()V",        (void *)&JVM_Yield}
};

JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
···
复制代码

The purpose registerNatives code in Java native method binding is registered in a manner to achieve dynamic binding by JNI_onload function known by the code above.

Remarks:

  1. JNINativeMethod structure
typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;
复制代码

  Jni.h more of source code, seen JNINativeMethodcomprising three elements: the method name, signature, Native function pointer, of the structure used in the methods described require registration information.
2. JNI_OnLoad and JNI_OnUnload function
JNI_OnLoad (): function is called VM execution System.loadLibrary (xxx) when the function, there are two important roles: Specifies the JNI version; told VM that the component uses a JNI version (if not provided JNI_OnLoad () function, VM will use the default oldest JNI 1.1 version), if you want to use the new version of JNI, for example JNI1.4 version, you must () function returns the JNI_OnLoad constant JNI_VERSION_1_4 (the constants defined in the jni.h ) to inform VM initialization setting, when the virtual machine to execute the System.loadLibrary () function will be executed immediately JNI_OnLoad () method, a variety of resources in the most appropriate initialization process, RegisterNatives here at this time.
JNI_OnUnload (): when releasing the VM component to be called, JNI_OnUnload () function returns the JNI_OnLoad () corresponds, therefore rehabilitate the cleaning process, the most appropriate resource release operation.
RegisterNatives 4. JNI in () method is a method for providing registration in JNI Native method, the method is declared in a JNINativeInterface_ structural body.
5. The type actually represents the JNIEnv Java environment, through which JNIEnv * pointer, Java code may operate end. For example, to create objects in the Java class, call Java object methods to obtain Java object attributes, and more. JNI JNIEnv pointer is passed to the local function implemented method Java code to operate the terminal.
6. The code example above is really running on Eclipse + 2015 Visual Studio, run the code when the need to pay attention to configure the environment.

5. References

  1. JNI study notes - the primary method by registering RegisterNatives
  2. Role Object class registerNatives method of in-depth presentation
  3. Jni call two methods javah and RegisterNatives
  4. Registration native code using RegisterNatives

Finally, thanks to the above authors were able to share such a good article made me learn.

Guess you like

Origin juejin.im/post/5dbb959151882523b5402c8f