Hi ClassLoader maintainers, This is a bug report.
(I think this is a hotspot bug, but it might be a core library ClassLoader bug or even a URLClassLoader bug) If you use RegisterNatives with a class loaded using URLClassLoader, you must call GetMethodID before RegisterNatives, or you get an UnsatisfiedLinkError as in the test case below (you will need to edit the below to fill in the location of your jni include directory and your libjvm.so) $ cat Test.java; echo ---------------------; cat jvm.cc; echo --------------------------; g++ -I$JDK/include -I$JDK/include/linux -ldl jvm.cc && ./a.out public class Test { public Test() { System.out.println("My class loader is " + getClass().getClassLoader()); testNative(); System.out.println("back to Java"); } public static native void testNative(); } --------------------- #include <cstdio> #include <dlfcn.h> #include <jni.h> JavaVM* jvm; JNIEnv* env; const char* libjvm = "$JDKDIR/libjvm.so"; const char* loader_url = "file://./"; const char* class_name = "Test"; void InitializeJVM() { JavaVMOption options[1]; options[0].optionString = (char*)"-Djava.class.path=."; //options[1].optionString = (char*)"-verbose:jni"; JavaVMInitArgs vm_args; vm_args.version = JNI_VERSION_1_2; vm_args.options = options; vm_args.nOptions = 2; vm_args.ignoreUnrecognized = JNI_TRUE; void* handle = dlopen(libjvm, RTLD_LAZY); if (handle == NULL) perror("dlopen"); jint JNICALL (*func_create_java_vm)(JavaVM**, void**, void*) = reinterpret_cast<jint JNICALL (*)(JavaVM**, void**, void*)> (dlsym(handle, "JNI_CreateJavaVM")); if (func_create_java_vm == NULL) perror("dlsym"); jint result = (*func_create_java_vm)(&jvm, (void**)(&env), &vm_args); } void TestNative(JNIEnv* env, jclass cls) { printf("Successfully called registered native method\n"); } void RegisterNativeMethod(jclass cls) { static const JNINativeMethod jni_method = { (char*)"testNative", (char*)"()V", (void*)&TestNative }; env->RegisterNatives(cls, &jni_method, 1); if (env->ExceptionCheck()) env->ExceptionDescribe(); } void Test() { // URL[] urls = {new URL(url)} jclass cls = env->FindClass("java/net/URL"); jmethodID method = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V"); jstring jurl_str = env->NewStringUTF(loader_url); jobject jurl = env->NewObject(cls, method, jurl_str); jobjectArray jurls = env->NewObjectArray(1, cls, jurl); // URLClassLoader loader = new URLClassLoaer(urls) cls = env->FindClass("java/net/URLClassLoader"); method = env->GetMethodID(cls, "<init>", "([Ljava/net/URL;)V"); jobject jloader = env->NewObject(cls, method, jurls); // Class cls = loader.loadClass(name) method = env->GetMethodID( cls, "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;"); jstring jname = env->NewStringUTF(class_name); cls = (jclass)env->CallObjectMethod(jloader, method, jname, (jboolean) true); method = env->GetMethodID(cls, "<init>", "()V"); if (env->ExceptionCheck()) env->ExceptionDescribe(); // RegisterNatives must be called after GetMethodID. // If the order is reversed, we get UnsatisfiedLinkError, // which seems like a bug. RegisterNativeMethod(cls); env->NewObject(cls, method); if (env->ExceptionCheck()) env->ExceptionDescribe(); } int main(int argc, char** argv) { InitializeJVM(); Test(); return 0; } -------------------------- My class loader is sun.misc.launcher$appclassloa...@1f7182c1 Successfully called registered native method back to Java Thanks, Martin