Part 40 - JNIEnv and JavaVM

The following describes two types of JNIEnv and JavaVM related to JNI mechanism.


JNIEnv is generally passed in by the virtual machine and thread related variables, that is, thread A cannot use the JNIEnv of thread B. As A structure, it defines the JNI system operation function. In the example introduced earlier, you can see the Java of C_ TestJNI_ Set() or Java_ TestJNI_ In the implementation of get() function, the type of the first parameter is JNIEnv *. JNIEnv is defined as follows:

Source: openjdk/hotspot/src/share/vm/prims/jni.h

struct JNIEnv_;

#ifdef __cplusplus
   typedef JNIEnv_   JNIEnv;
   typedef const struct JNINativeInterface_ *JNIEnv;

The implementation of JNIEnv in C language environment is different from that in C + + language environment. Defined in C as JNI nativeinterface_, Defined in C + + as JNIEnv_.

JNIEnv_ Structure is defined as follows:

struct JNIEnv_ {
    const struct JNINativeInterface_ *functions;
#ifdef __cplusplus
    jint GetVersion() {
        return functions->GetVersion(this);
    jclass DefineClass(const char *name, jobject loader, const jbyte *buf,jsize len) {
        return functions->DefineClass(this, name, loader, buf, len);
    jclass FindClass(const char *name) {
        return functions->FindClass(this, name);

At JNIEnv_ A functions variable is defined in, which points to JNI nativeinterface_ Pointer to. Therefore, when we write a native function and receive a variable env of type JNIEnv *, we can call the function in JNIEnv in the following way (to be exact, we call the function through the function pointer, because the data structure of JNIEnv aggregates the function pointers of all JNI functions). We can call it in C + + in the following way:

env->FindClass("java/lang/String")         // Writing in C + +

In C, it can be called as follows:

(*env)->FindClass(env, "java/lang/String") // Writing in C 

Since the variable functions is defined in the structure JNIEnv_ So we can get the value of the functions variable through * env, and then call the corresponding function through the function pointer in JNI nativeinterface.

JNINativeInterface_ Structure is defined as follows:

struct JNINativeInterface_ {
    void *reserved0;
    void *reserved1;
    void *reserved2;
    void *reserved3;

    jint (JNICALL *GetVersion) (JNIEnv *env);

    jclass (JNICALL *DefineClass) (JNIEnv *env, const char *name, jobject loader, const jbyte *buf,jsize len);
    jclass (JNICALL *FindClass) (JNIEnv *env, const char *name);
    // ...

Let's introduce JNIEnv_ The function corresponding to the function pointer saved in the structure is as follows:

(1) The jclass type represents a Class class in Java. JNIEnv_ There are several simple functions in the structure (in fact, the corresponding function is called through the function pointer, which is directly expressed as a function, and a similar expression is adopted later) to obtain jclass:

  • jclass FindClass(const char* clsName): get the jclass by the name of the class (the full name of the class. In this case, the package name is not distinguished by the. Sign, but by /), for example: jclass STR = env - > findclass ("Java / Lang / String"); Get the class instance of String object in Java;
  • Jclass getobjectclass (jobobject obj): get jclass through object instance, which is equivalent to getClass() method in Java;
  • jclass GetSuperClass(jclass obj): the jclass instance of its parent class can be obtained through jclass.

(2) Reference related API s

  • Jobobject newglobalref (jnienv * Env, jobobject obj): create a global reference to obj, which can be local or global. The global reference can only be released by calling DeleteGlobalRef() on the display;
  • Void deleteglobalref (jnienv * Env, job globalref): deletes a global reference;
  • Jobobject newlocalref (jnienv * Env, jobobject ref): create a local reference to the instance ref;
  • Void deletelocalref (jnienv * Env, jobobject localref): deletes a local reference;
  • Jint ensureliocalcapacity (jnienv * Env, jint capacity): evaluates whether the current thread can create a specified number of local references. If it can return 0, otherwise it returns a negative number and throws an OutOfMemoryError exception. Before executing the local method, the JVM will automatically evaluate whether the current thread can create at least 16 local references. The JVM allows the creation of local references that exceed the evaluation quantity. If too many are created, resulting in insufficient memory for the JVM, the JVM will throw a FatalError;
  • jint PushLocalFrame(JNIEnv *env, jint capacity): create a new Frame that supports the creation of a given number of local references. If 0 can be returned, otherwise a negative number will be returned and an OutOfMemoryError exception will be thrown. Note that the local reference created in the previous Frame is still valid in the new Frame
  • Jobobject PopLocalFrame (jnienv * Env, jobobject result): pop up the current local reference Frame, and then release all local references therein. If the result is not NULL, the local reference of the object in the previous Frame push ed before the current Frame will be returned. PushLocalFrame and PopLocalFrame are used together. It is common that local references generated during method execution need to be released as soon as possible;  
  • Jweek newweakglobalref (jnienv * Env, jobobject obj): create a weak global reference to the object obj. Jweek is the alias of jobobject. If obj is null, null will be returned. If memory is insufficient, an OutOfMemoryError exception will be thrown;
  • void DeleteWeakGlobalRef(JNIEnv *env, jweak obj): deletes weak global references;
  • Jobjectreftype getobjectreftype (jnienv * Env, jobobject obj): get the reference type of an object reference, introduced in JDK1.6.

(3) Get jfield ID and jmethodID

A common application for accessing Java side code in C/C + + local code is to obtain class fields and call class methods. In order to represent fields and methods in C/C + +, JNI defines jfield ID and jmethodID types in jni.h header file to represent Java side fields and methods respectively

When accessing or setting a Java field, we must first obtain the jfield ID representing the Java field in the local code, and then we can operate Java properties in the local code. Similarly, when calling a method on the Java side, you also need to obtain the jmethod ID representing the method before calling a Java method. The related functions are as follows:

  • Jfield ID getfieldid (jclass clazz, const char * name, const char * sign) to obtain the jfield ID of the instance field;
  • Jfield ID getstaticfieldid (jclass clazz, const char * name, const char * SIG) to obtain the jfield ID of the static field;
  • Jmethodidgetmethodid (jclass clazz, const char * name, const char * SIG) to obtain the jmethodidid of the instance method;
  • Jmethodidgetstaticmethodid (jclass clazz, const char * name, const char * SIG) to obtain the jmethodidid of the static method.

More about JNI function parameters: JNI Functions 

2. Load and unload local methods

When writing the implementation of C/C + + language corresponding to JNI, you can also implement the following functions:

JNIEXPORT jint JNICALL  JNI_OnLoad(JavaVM *vm, void *reserved);

JNIEXPORT void JNICALL  JNI_OnUnload(JavaVM *vm, void *reserved);

JVM provides a way to allow you to do something you want to do when loading dynamic link library files, that is, JNI_OnLoad() function. When explicitly uninstalling a local library, you will see JNI_ The onunload() function is called. JNI_ The onload() function is called when the dynamic library is loaded, and JNI_ The onunload() function is called when the local library is unloaded. Therefore, these two functions are the two most important functions to manage the life cycle of a local library.

You can use this set of functions to link Java methods to local C functions. An example is as follows:

package com.classloading;
class NativeLib{ public static native String getName(int number); }

According to the agreement, the following statement shall be made in the local file:

JNIEXPORT jstring JNICALL Java_com_classloading_NativeLib_getName(JNIEnv *env,jobject thiz,jint number);

However, now we declare a local function that is not named according to the JNI specification, as follows:

JNIEXPORT jstring JNICALL getName(JNIEnv *env, jclass clazz);

The mapping between Java methods and local functions must be realized by dynamic association. The code is as follows:

extern "C"
JNIEXPORT jstring JNICALL getName(JNIEnv *env, jobject thiz, int number) { printf("number is %d",number); return env->NewStringUTF("hello world"); } static const char *CLASS_NAME = "com/classloading/NativeLib"; // Class name static JNINativeMethod method = { // Local method description "getName", // Java method name "(I)Ljava/lang/String;", // Java method signature (void *) getName // Bind to the corresponding local function }; static bool bindNative(JNIEnv *env) { jclass clazz; clazz = env->FindClass(CLASS_NAME); if (clazz == NULL) { return false; } return env->RegisterNatives(clazz, &method, 1) == 0; } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; jint result = -1; if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return result; } bool res = bindNative(env); printf("bind result is %s",res?"ok":"error"); // Returns the version of JNI return JNI_VERSION_1_6; }

Note: JNI_OnLoad() function can only exist in each dynamic link library. javah is often used to generate JNI header files, and then implement their own JNI functions in strict accordance with the function names declared in the header files. This method is more traditional, and the defined format and even the names must comply with the specifications. However, the JVM also provides the register native() function to manually register the native method. The called RegisterNatives() function is declared as follows:

jint RegisterNatives(jclass clazz, const JNINativeMethod *methods,jint nMethods)

Now let's standardize the terms:


The function is parsed as follows:

1. methods is a two-dimensional array (the method is equivalent to a two-dimensional array after taking the address), which represents the implementation function corresponding to each native method in the clazz. In the following example, a native method retrievedirections, the return value is assertionstatsdirections, and the corresponding executed local function is the JVM_AssertionStatusDirectives. Examples are as follows:


static JNINativeMethod methods[] = {
    {"retrieveDirectives",  "()Ljava/lang/AssertionStatusDirectives;", (void *)&JVM_AssertionStatusDirectives}

JNIEXPORT void JNICALL Java_java_lang_ClassLoader_registerNatives(JNIEnv *env, jclass cls) {
    (*env)->RegisterNatives(env, cls, methods,

2. The following nMethods represent the number of native methods to be specified

Finally, uninstall the native method. The implementation code is as follows:

static bool unBindNative(JNIEnv *env) {
    jclass clazz;
    clazz = env->FindClass(CLASS_NAME);
    if (clazz == NULL) {
        return false;
    return env->UnregisterNatives(clazz) == 0;

JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    jint result = -1;

    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
    bool res = unBindNative(env);
    sprintf("unbind result is %s", res ? "ok" : "error");



JavaVM is the representation of virtual machine in JNI. There is only one JavaVM instance in a JVM, which is shared by threads. A Java virtual machine instance can be obtained through JNIEnv, and its functions are as follows:

jint GetJavaVM(JNIEnv *env, JavaVM **vm);

vm is used to store the obtained virtual machine pointer. 0 is returned for success and other values are returned for failure.

struct JNIInvokeInterface_;

struct JavaVM_;

#ifdef __cplusplus
  typedef JavaVM_ JavaVM;
  typedef const struct JNIInvokeInterface_ *JavaVM;

You can see that the implementation in C language environment is different from that in C + + language environment. JNIInvokeInterface_ Java VM_ The definition of is as follows:

struct JNIInvokeInterface_ {
    void *reserved0;
    void *reserved1;
    void *reserved2;

    jint (JNICALL *DestroyJavaVM)(JavaVM *vm);

    jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args);

    jint (JNICALL *DetachCurrentThread)(JavaVM *vm);

    jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version);

    jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args);

struct JavaVM_ {
    const struct JNIInvokeInterface_ *functions;
#ifdef __cplusplus

    jint DestroyJavaVM() {
        return functions->DestroyJavaVM(this);
    jint AttachCurrentThread(void **penv, void *args) {
        return functions->AttachCurrentThread(this, penv, args);
    jint DetachCurrentThread() {
        return functions->DetachCurrentThread(this);

    jint GetEnv(void **penv, jint version) {
        return functions->GetEnv(this, penv, version);
    jint AttachCurrentThreadAsDaemon(void **penv, void *args) {
        return functions->AttachCurrentThreadAsDaemon(this, penv, args);

As you can see, JNIInvokeInterface_ Java VM_ The definition of is very similar to JNI native interface_ With JNIEnv_, Its usage is also very similar. I won't introduce it too much here.

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.




Posted on Wed, 10 Nov 2021 16:54:13 -0500 by LowEndTheory