Skip to content

Instantly share code, notes, and snippets.

@dhilst
Last active October 31, 2016 22:06
Show Gist options
  • Save dhilst/29cd74f42bf2d319ce793f1cd0c9b399 to your computer and use it in GitHub Desktop.
Save dhilst/29cd74f42bf2d319ce793f1cd0c9b399 to your computer and use it in GitHub Desktop.
// In this example I will show how to cache classes, method and field IDs. Caching
// these lookups can improve performance of native calls by safing native code
// from extra JNI calls.
public class JNICache {
// Here we load our native library at a static block. This ensures that the library
// is loaded as soon as the class is loaded. This will make caching simpler and safer.
static {
System.loadLibrary("cache");
}
// These fields will be accessed by native getters. Their IDs are cached at library
// load time.
int intField;
String stringField;
// This method will be called by native code. It's methodID will be cached at library
// load time.
static void calledFromNative() {
System.out.println("Hello from java");
}
// The native getters. Access object's fields.
native String getString();
native int getInt();
// A simple constructor to initialize our fields.
JNICache(int i, String s) {
intField = i;
stringField = s;
}
public static void main(String[] args) {
// Instantiate an object.
JNICache jc = new JNICache(args[0].length(), args[0]);
// Call it's native getters
System.out.println("JNICache { " + jc.getInt() + ", " + jc.getString() + "}");
}
}
// vim: set ts=4:sw=4
#include "JNICache.h"
#include <iostream>
#include <stdexcept>
using namespace std;
// This is our cache. The variables are initialized
// at JNI_OnLoad and used by native methods.
static jclass JNICache_cls = NULL;
static jmethodID JNICache_calledFromNative_mid = NULL;
static jfieldID JNICache_string_fid = NULL;
static jfieldID JNICache_int_fid = NULL;
extern "C"
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env;
if (vm->AttachCurrentThread((void **)&env, NULL) != JNI_OK) {
cerr << "AttachCurrentThread failed" << endl;
return -1;
}
// We call FindClass to get a reference to JNICache class.
// That reference will be valid for the life time of JNIEnv.
jclass cls = env->FindClass("JNICache");
if (cls == NULL) {
cerr << "FindClass failed for JNICache class" << endl;
exit(-1);
}
// To make the reference live forever we need to create a
// global reference. Here [1] you can read more about references.
// [1] http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#global_local
JNICache_cls = (jclass) env->NewGlobalRef(cls);
if (JNICache_cls == NULL) {
cerr << "NewGlobalRef failed for JNICache class" << endl;
exit(-1);
}
// Method and field IDs can be simply saved to global variables
// since they don't depend on JNIEnv life time.
JNICache_calledFromNative_mid = env->GetStaticMethodID(JNICache_cls, "calledFromNative", "()V");
if (!JNICache_calledFromNative_mid) {
cerr << "Cache failed for calledFromNative method" << endl;
exit(-1);
}
JNICache_string_fid = env->GetFieldID(JNICache_cls, "stringField", "Ljava/lang/String;");
if (!JNICache_string_fid) {
cerr << "Cache failed for JNICache_string_fid method" << endl;
exit(-1);
}
JNICache_int_fid = env->GetFieldID(JNICache_cls, "intField", "I");
if (!JNICache_int_fid) {
cerr << "Cache failed for JNICache_int_fid method" << endl;
exit(-1);
}
vm->DetachCurrentThread();
return JNI_VERSION_1_1;
}
extern "C"
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)
{
JNIEnv* env;
if (vm->AttachCurrentThread((void **)&env, NULL) != JNI_OK)
return;
// Delete the global reference so we don't create memory leaks.
env->DeleteGlobalRef(JNICache_cls);
vm->DetachCurrentThread();
}
extern "C"
JNIEXPORT jstring JNICALL Java_JNICache_getString
(JNIEnv* env, jobject obj)
{
// This call use the cached method, saving us from calling
// FindClass and GetStaticMethodID. In this example the
// class could be retrieved f
// with obj as its parameter.
// Here the cached field id is used.
env->CallStaticVoidMethod(JNICache_cls, JNICache_calledFromNative_mid);
return reinterpret_cast<jstring>(env->GetObjectField(obj, JNICache_string_fid));
}
extern "C"
JNIEXPORT jint JNICALL Java_JNICache_getInt
(JNIEnv* env, jobject obj)
{
env->CallStaticVoidMethod(JNICache_cls, JNICache_calledFromNative_mid);
return env->GetIntField(obj, JNICache_int_fid);
}
JAVA_HOME = /usr/lib/jvm/java-1.8.0-openjdk-amd64
TARGET := JNICache
TARGET_NATIVE := libcache
CXXFLAGS += -Wall -std=c++11
CXXFLAGS += -I. -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux
all: libcache.so
clean:
rm JNICache.class libcache.so JNICache.h
run:
java -Djava.library.path=. JNICache $(TARGET_ARGS)
libcache.o: JNICache.h libcache.cc
libcache.so: libcache.o
JNICache.h: JNICache.class
JNICache.class: JNICache.java
%.o: %.cc
$(CXX) $(CXXFLAGS) $(LDFLAGS) -fPIC -c $<
%.so: %.o
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -o $@ $<
%.class: %.java
javac $<
%.h: %.class
javah -cp . $(<:.class=)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment