Search code examples
typesjvmjava-native-interface

JNI.h jtypes and relations


I saw the code of jni.h here : http://xdprof.sourceforge.net/doxygen/jni_8h-source.html

and i want to know what is this:

class _jobject {};
00053 class _jclass : public _jobject {};
00054 class _jthrowable : public _jobject {};
00055 class _jstring : public _jobject {};
00056 class _jarray : public _jobject {};
00057 class _jbooleanArray : public _jarray {};
00058 class _jbyteArray : public _jarray {};
00059 class _jcharArray : public _jarray {};
00060 class _jshortArray : public _jarray {};
00061 class _jintArray : public _jarray {};
00062 class _jlongArray : public _jarray {};
00063 class _jfloatArray : public _jarray {};
00064 class _jdoubleArray : public _jarray {};
00065 class _jobjectArray : public _jarray {};

What is the relation for example between _jobject empty class and Object class - "Object.h/Object.cpp" (internally in JVM) and why these classes are empty?


Solution

  • These classes are needed only to maintain hierarchy among corresponding JNI types:

    jobject <- jarray <- jintArray etc.
    

    So that jarray can be used, for example, in a function that accepts jobject.

    The contents of the classes does not matter. JNI types are meant to be opaque for the application. There is no direct relation between JNI types and Java classes. This relation is hidden inside JVM code, i.e. if some JNI function expects jstring argument, JVM explicitly treats that argument as a handle to java.lang.String instance. E.g. from HotSpot sources:

    JNI_QUICK_ENTRY(jsize, jni_GetStringLength(JNIEnv *env, jstring string))
      ...
      oop s = JNIHandles::resolve_non_null(string);
      if (java_lang_String::value(s) != NULL) {       <-- explicitly cast a handle
        ret = java_lang_String::length(s);
      }
    

    The type safety of such operations may or may not be enforced in runtime depending on JVM implementation. E.g. HotSpot can simply crash if you pass some crafted jstring argument which is not actually a handle to java.lang.String object.

    EDIT

    jstring is a pointer to a pointer to a Java String instance (note double indirection). JVM does not access the value of class _jstring but rather treats it as a pointer to the actual instance (see the above example).

      java.lang.String             JNIHandleBlock                    jstring string
      ------------------           -----------------------           -----------------
     |  0: (header)     |<---\    | oop[0]                |     /---| class _jclass * |
     |  8: (class ptr)  |     \---| oop[1]  <------------------/     -----------------
     | 12: char[] value |         | ...                   |
     | 16: int hash     |         | oop[31]               |
     | 24: (padding)    |         |                       |
      ------------------          | int _top              |
                                  | JNIHandleBlock* _next |
                                  | ...                   |
                                   -----------------------
    

    When working with jstring, HotSpot JVM first resolves a handle to an oop (ordinary object pointer):

        oop s = JNIHandles::resolve_non_null(string);
        // This is effectively the same as
        //     oop s = *(oop*)string;
    

    Then to read a field of an oop, JVM uses raw pointer arithmetic (assuming it knows that the field value has the offset 12 from the beginning of java.lang.String instance):

        java_lang_String::value(s)
        // Effectively the same as
        //     s->obj_field(12)
        // which is expanded to something like
        //     *(oop*)((intptr_t)s + 12)
    

    Other JVM implementations may treat class _jstring differently. The idea is to make JNI types opaque so that different JVMs may implement JNI handles in their own way without need to recompile the code that uses those handles. That's why the contents of the classes does not matter - this is just the declaration, not the actual definition of the types.