Efficient JNI programming IV: Wrapping native data objects

Everytime I write an article about JNI, there’s some more stuff that comes to mind. This time about wrapping native data structures in a Java object. This is something very basic which almost every JNI program has to deal with, so I’ll have a look at a couple of approaches.

Casting pointers to long

The most straightforward, easy and probably most common approach to this problem is to simply cast the pointer to the data object to a jint and store it into a field of the wrapper object. In order to stay compatible with 64 bit pointers on newer systems, you really want to cast to a jlong instead. This is certainly the approach with the least overhead and least storage demands. But it has some (slight) disadvantages that you might consider. Let’s look at

Wrapping pointers in special objects

Some people would argue that exposing a pointer as long field (even if it’s private) is a little dangerous, because this means that the actual address could be accidentally changed from the Java side. This is why we have a bunch of special classes plus helper code in GNU Classpath. There, the pointer is stored in an instance of gnu.classpath.Pointer, an abstract class with two concrete subclasses, gnu.classpath.Pointer32 and gnu.classpath.Pointer64, each of which implements a 32 bit and 64 bit pointer respectively. This is accompanied by a couple of native helper functions to allow easy access to the actual pointer. This way it is possible to store a native pointer in an opaque way on the Java side. The tradeoff is a slightly higher memory demand (+1 Java object) and slightly more overhead to access the pointer. But it doesn’t hurt that much either (all are small O(1) operations).

Direct ByteBuffer

This is a cool trick that I learned from JOGL. From JDK4 onwards you can quickly forget the last paragraph, because you don’t need to add any new classes or helper functions, because it’s all right there in the JDK. It might not be obvious at first glance, but the direct ByteBuffer serves the exact same purpose, and does even more. Maybe the name ByteBuffer is a little misleading for this use case, but alas, it’s basically a native pointer and some bookkeeping information. There’s even the required helper methods in JNI, NewDirectByteBuffer(), GetDirectBufferAddress() and GetDirectBufferCapacity(). Suppose you have a native datastructure of type MyNativeStruct and want to wrap it as a direct ByteBuffer, you’d do:

MyNativeStruct* data; // Initialized elsewhere.
jobject bb = (*env)->NewDirectByteBuffer(env, (void*) data, sizeof(MyNativeStruct));

Later when you need to access the pointer, you can do this:

jobject bb; // Initialized elsewhere.
MyNativeStruct* data = (MyNativeStruct*) (*env)->GetDirectBufferAddress(env, bb);

Easy, isn’t it? But it’s even better. Using this approach it is possible to actually access the native data structure from Java. You need to know the data layout of the native data and can then access it from Java using the ByteBuffer methods.

Suppose you have such a data structure:

struct {
  int exampleInt;
  short exampleShort;
} MyNativeStruct;

You could provide accessor methods on the java side like in the following example.

public int getExampleInt() {
  return bb.getInt(0);
}

public short getExampleShort() {
  return bb.getShort(4);
}

The ByteBuffer code would even sort out endianess problems for you. Neat, isn’t it?

Also check out part I, part II and part III of this series too.

Advertisements

4 Responses to Efficient JNI programming IV: Wrapping native data objects

  1. Pingback: Olkenava » Efficient JNI programming IV: Wrapping native data objects

  2. Satyavathi says:

    There is native pointer that points to Huge memory.
    Will there be performance pitfall, if all the info in memory to a java object, and do processing on java code? or is it better to do processing on the fly by using the native ptr – by invoking the native methods many times.

  3. Satyavathi says:

    there is a pointer that points to millions entries in the memory.
    It is very easy to get nextelement in c, by just incrementing the pointer.
    If you want to get the million entries that are there in memory to a java object, this can be done in a single JNI call, poplating the java object.
    To get into the java object you need to traverse over all the entries once, and if these entries are to be processed then one more scan is needed on the java object. Processing can be done only on the java side.

    Therefore 2 scans on millions of entries.

    In the other scenario, do a single scan over the entries, but call million times the JNI method.

    Do you still recommend to get everything on to the java side and process.

  4. Satyavathi says:

    Can DirectByteBuffer be used for C++ classes also, just like we use it for C Structures. Any pitfalls if it can be used?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: