It's been a while since I worked with this stuff, but I would have done a:

   jclass cls = env->FindClass("Test");

Which, if I recall, FindClass actually initializes the class.

Just a stab in the dark.

-kto


On Jun 9, 2010, at 12:39 PM, Martin Buchholz wrote:

David, Thanks for the bug archaeology.  Judging by the other
commenters, this bug is a regression from jdk5, and has cost many
other users hours of frustrating debugging time, so I think an
increase to P2 is in order.  Since it used to work, it shouldn't be
too hard to fix - all you need to do is to properly track registered
native methods.

Sorry for not doing this kind of research myself - it's just too
painful without my beloved jbugs script :-(

Here's an improved version of our test case, with
-XX:+PrintJNIResolving output added,
that you might add to the bug report.

$ cat Test.java; echo ---------------------; cat jvm.cc; echo
--------------------------; JDK=`jver 1.7.0-b96`; g++ -I$JDK/include
-I$JDK/include/linux  -ldl -DJDK=\"$JDK\" 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 <cstdlib>
#include <dlfcn.h>
#include <jni.h>

JavaVM* jvm;
JNIEnv* env;
const char* libjvm = JDK "/jre/lib/amd64/server/libjvm.so";
const char* loader_url = "file://./";
const char* class_name = "Test";

#define countof(array) (sizeof(array)/sizeof(array[0]))

void InitializeJVM() {
 JavaVMOption options[] = {
   { (char*)"-Djava.class.path=.", NULL },
   { (char*)"-XX:+PrintJNIResolving", NULL },
   //{ (char*)"-verbose:jni", NULL },
 };

 JavaVMInitArgs vm_args;
 vm_args.version = JNI_VERSION_1_2;
 vm_args.options = options;
 vm_args.nOptions = countof(options);
 vm_args.ignoreUnrecognized = JNI_TRUE;

 void* handle = dlopen(libjvm, RTLD_LAZY);
 if (!handle) { fprintf(stderr, "%s\n", dlerror()); exit(1); }

 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) { fprintf(stderr, "%s\n", dlerror()); exit(1); }

 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);

 // RegisterNatives must be called after GetMethodID.
 // If the order is reversed, we get UnsatisfiedLinkError,
 // which seems like a bug.
 RegisterNativeMethod(cls);

 method = env->GetMethodID(cls, "<init>", "()V");
 if (env->ExceptionCheck()) env->ExceptionDescribe();

 env->NewObject(cls, method);
 if (env->ExceptionCheck()) env->ExceptionDescribe();
}

int main(int argc, char** argv) {
 InitializeJVM();
 Test();

 return 0;
}
--------------------------
[Dynamic-linking native method java.lang.Object.registerNatives ... JNI]
[Registering JNI native method java.lang.Object.hashCode]
[Registering JNI native method java.lang.Object.wait]
[Registering JNI native method java.lang.Object.notify]
[Registering JNI native method java.lang.Object.notifyAll]
[Registering JNI native method java.lang.Object.clone]
[Dynamic-linking native method java.lang.System.registerNatives ... JNI]
[Registering JNI native method java.lang.System.currentTimeMillis]
[Registering JNI native method java.lang.System.nanoTime]
[Registering JNI native method java.lang.System.arraycopy]
[Dynamic-linking native method java.lang.Thread.registerNatives ... JNI]
[Registering JNI native method java.lang.Thread.start0]
[Registering JNI native method java.lang.Thread.stop0]
[Registering JNI native method java.lang.Thread.isAlive]
[Registering JNI native method java.lang.Thread.suspend0]
[Registering JNI native method java.lang.Thread.resume0]
[Registering JNI native method java.lang.Thread.setPriority0]
[Registering JNI native method java.lang.Thread.yield]
[Registering JNI native method java.lang.Thread.sleep]
[Registering JNI native method java.lang.Thread.currentThread]
[Registering JNI native method java.lang.Thread.countStackFrames]
[Registering JNI native method java.lang.Thread.interrupt0]
[Registering JNI native method java.lang.Thread.isInterrupted]
[Registering JNI native method java.lang.Thread.holdsLock]
[Registering JNI native method java.lang.Thread.getThreads]
[Registering JNI native method java.lang.Thread.dumpThreads]
[Dynamic-linking native method
java.security.AccessController.getStackAccessControlContext ... JNI]
[Dynamic-linking native method
java.security.AccessController.getInheritedAccessControlContext ...
JNI]
[Dynamic-linking native method java.lang.ClassLoader.registerNatives ... JNI] [Registering JNI native method java.lang.ClassLoader.retrieveDirectives]
[Dynamic-linking native method
java.security.AccessController.doPrivileged ... JNI]
[Dynamic-linking native method java.lang.Class.registerNatives ... JNI]
[Registering JNI native method java.lang.Class.getName0]
[Registering JNI native method java.lang.Class.getSuperclass]
[Registering JNI native method java.lang.Class.getInterfaces]
[Registering JNI native method java.lang.Class.getClassLoader0]
[Registering JNI native method java.lang.Class.isInterface]
[Registering JNI native method java.lang.Class.getSigners]
[Registering JNI native method java.lang.Class.setSigners]
[Registering JNI native method java.lang.Class.isArray]
[Registering JNI native method java.lang.Class.isPrimitive]
[Registering JNI native method java.lang.Class.getComponentType]
[Registering JNI native method java.lang.Class.getModifiers]
[Registering JNI native method java.lang.Class.getDeclaredFields0]
[Registering JNI native method java.lang.Class.getDeclaredMethods0]
[Registering JNI native method java.lang.Class.getDeclaredConstructors0]
[Registering JNI native method java.lang.Class.getProtectionDomain0]
[Registering JNI native method java.lang.Class.setProtectionDomain0]
[Registering JNI native method java.lang.Class.getDeclaredClasses0]
[Registering JNI native method java.lang.Class.getDeclaringClass]
[Registering JNI native method java.lang.Class.getGenericSignature]
[Registering JNI native method java.lang.Class.getRawAnnotations]
[Registering JNI native method java.lang.Class.getConstantPool]
[Registering JNI native method java.lang.Class.desiredAssertionStatus0]
[Registering JNI native method java.lang.Class.getEnclosingMethod0]
[Dynamic-linking native method java.lang.Class.getPrimitiveClass ... JNI] [Dynamic-linking native method java.lang.System.initProperties ... JNI] [Dynamic-linking native method sun.misc.Unsafe.registerNatives ... JNI]
[Registering JNI native method sun.misc.Unsafe.getLoadAverage]
[Dynamic-linking native method java.lang.Throwable.fillInStackTrace ... JNI]
[Registering JNI native method sun.misc.Unsafe.copyMemory]
[Registering JNI native method sun.misc.Unsafe.setMemory]
[Registering JNI native method sun.misc.Unsafe.getObject]
[Registering JNI native method sun.misc.Unsafe.putObject]
[Registering JNI native method sun.misc.Unsafe.getObjectVolatile]
[Registering JNI native method sun.misc.Unsafe.putObjectVolatile]
[Registering JNI native method sun.misc.Unsafe.getBoolean]
[Registering JNI native method sun.misc.Unsafe.putBoolean]
[Registering JNI native method sun.misc.Unsafe.getBooleanVolatile]
[Registering JNI native method sun.misc.Unsafe.putBooleanVolatile]
[Registering JNI native method sun.misc.Unsafe.getByte]
[Registering JNI native method sun.misc.Unsafe.putByte]
[Registering JNI native method sun.misc.Unsafe.getByteVolatile]
[Registering JNI native method sun.misc.Unsafe.putByteVolatile]
[Registering JNI native method sun.misc.Unsafe.getShort]
[Registering JNI native method sun.misc.Unsafe.putShort]
[Registering JNI native method sun.misc.Unsafe.getShortVolatile]
[Registering JNI native method sun.misc.Unsafe.putShortVolatile]
[Registering JNI native method sun.misc.Unsafe.getChar]
[Registering JNI native method sun.misc.Unsafe.putChar]
[Registering JNI native method sun.misc.Unsafe.getCharVolatile]
[Registering JNI native method sun.misc.Unsafe.putCharVolatile]
[Registering JNI native method sun.misc.Unsafe.getInt]
[Registering JNI native method sun.misc.Unsafe.putInt]
[Registering JNI native method sun.misc.Unsafe.getIntVolatile]
[Registering JNI native method sun.misc.Unsafe.putIntVolatile]
[Registering JNI native method sun.misc.Unsafe.getLong]
[Registering JNI native method sun.misc.Unsafe.putLong]
[Registering JNI native method sun.misc.Unsafe.getLongVolatile]
[Registering JNI native method sun.misc.Unsafe.putLongVolatile]
[Registering JNI native method sun.misc.Unsafe.getFloat]
[Registering JNI native method sun.misc.Unsafe.putFloat]
[Registering JNI native method sun.misc.Unsafe.getFloatVolatile]
[Registering JNI native method sun.misc.Unsafe.putFloatVolatile]
[Registering JNI native method sun.misc.Unsafe.getDouble]
[Registering JNI native method sun.misc.Unsafe.putDouble]
[Registering JNI native method sun.misc.Unsafe.getDoubleVolatile]
[Registering JNI native method sun.misc.Unsafe.putDoubleVolatile]
[Registering JNI native method sun.misc.Unsafe.getByte]
[Registering JNI native method sun.misc.Unsafe.putByte]
[Registering JNI native method sun.misc.Unsafe.getShort]
[Registering JNI native method sun.misc.Unsafe.putShort]
[Registering JNI native method sun.misc.Unsafe.getChar]
[Registering JNI native method sun.misc.Unsafe.putChar]
[Registering JNI native method sun.misc.Unsafe.getInt]
[Registering JNI native method sun.misc.Unsafe.putInt]
[Registering JNI native method sun.misc.Unsafe.getLong]
[Registering JNI native method sun.misc.Unsafe.putLong]
[Registering JNI native method sun.misc.Unsafe.getFloat]
[Registering JNI native method sun.misc.Unsafe.putFloat]
[Registering JNI native method sun.misc.Unsafe.getDouble]
[Registering JNI native method sun.misc.Unsafe.putDouble]
[Registering JNI native method sun.misc.Unsafe.getAddress]
[Registering JNI native method sun.misc.Unsafe.putAddress]
[Registering JNI native method sun.misc.Unsafe.allocateMemory]
[Registering JNI native method sun.misc.Unsafe.reallocateMemory]
[Registering JNI native method sun.misc.Unsafe.freeMemory]
[Registering JNI native method sun.misc.Unsafe.objectFieldOffset]
[Registering JNI native method sun.misc.Unsafe.staticFieldOffset]
[Registering JNI native method sun.misc.Unsafe.staticFieldBase]
[Registering JNI native method sun.misc.Unsafe.ensureClassInitialized]
[Registering JNI native method sun.misc.Unsafe.arrayBaseOffset]
[Registering JNI native method sun.misc.Unsafe.arrayIndexScale]
[Registering JNI native method sun.misc.Unsafe.addressSize]
[Registering JNI native method sun.misc.Unsafe.pageSize]
[Registering JNI native method sun.misc.Unsafe.defineClass]
[Registering JNI native method sun.misc.Unsafe.defineClass]
[Registering JNI native method sun.misc.Unsafe.allocateInstance]
[Registering JNI native method sun.misc.Unsafe.monitorEnter]
[Registering JNI native method sun.misc.Unsafe.monitorExit]
[Registering JNI native method sun.misc.Unsafe.tryMonitorEnter]
[Registering JNI native method sun.misc.Unsafe.throwException]
[Registering JNI native method sun.misc.Unsafe.compareAndSwapObject]
[Registering JNI native method sun.misc.Unsafe.compareAndSwapInt]
[Registering JNI native method sun.misc.Unsafe.compareAndSwapLong]
[Registering JNI native method sun.misc.Unsafe.putOrderedObject]
[Registering JNI native method sun.misc.Unsafe.putOrderedInt]
[Registering JNI native method sun.misc.Unsafe.putOrderedLong]
[Registering JNI native method sun.misc.Unsafe.park]
[Registering JNI native method sun.misc.Unsafe.unpark]
[Dynamic-linking native method java.lang.Float.floatToRawIntBits ... JNI] [Dynamic-linking native method java.lang.Double.doubleToRawLongBits ... JNI] [Dynamic-linking native method sun.reflect.Reflection.getCallerClass ... JNI]
[Dynamic-linking native method java.lang.String.intern ... JNI]
[Dynamic-linking native method java.lang.Object.getClass ... JNI]
[Dynamic-linking native method java.lang.Class.forName0 ... JNI]
[Dynamic-linking native method
sun.reflect.Reflection.getClassAccessFlags ... JNI]
[Dynamic-linking native method
sun.reflect.NativeConstructorAccessorImpl.newInstance0 ... JNI]
[Dynamic-linking native method sun.misc.VM.initialize ... JNI]
[Dynamic-linking native method java.io.FileSystem.getFileSystem ... JNI]
[Dynamic-linking native method java.io.UnixFileSystem.initIDs ... JNI]
[Dynamic-linking native method java.lang.System.mapLibraryName ... JNI]
[Dynamic-linking native method
java.io.UnixFileSystem.getBooleanAttributes0 ... JNI]
[Dynamic-linking native method java.io.UnixFileSystem.canonicalize0 ... JNI] [Dynamic-linking native method java.lang.ClassLoader $NativeLibrary.load ... JNI] [Dynamic-linking native method java.io.FileInputStream.initIDs ... JNI]
[Dynamic-linking native method java.io.FileDescriptor.initIDs ... JNI]
[Dynamic-linking native method java.io.FileOutputStream.initIDs ... JNI]
[Dynamic-linking native method java.lang.System.setIn0 ... JNI]
[Dynamic-linking native method java.lang.System.setOut0 ... JNI]
[Dynamic-linking native method java.lang.System.setErr0 ... JNI]
[Dynamic-linking native method sun.misc.Signal.findSignal ... JNI]
[Dynamic-linking native method sun.misc.Signal.handle0 ... JNI]
[Dynamic-linking native method java.lang.Runtime.maxMemory ... JNI]
[Dynamic-linking native method java.lang.Compiler.registerNatives ... JNI]
[Registering JNI native method java.lang.Compiler.compileClass]
[Registering JNI native method java.lang.Compiler.compileClasses]
[Registering JNI native method java.lang.Compiler.command]
[Registering JNI native method java.lang.Compiler.enable]
[Registering JNI native method java.lang.Compiler.disable]
[Dynamic-linking native method java.lang.ClassLoader.getCaller ... JNI] [Dynamic-linking native method java.lang.ClassLoader $NativeLibrary.find ... JNI]
[Dynamic-linking native method
java.security.AccessController.doPrivileged ... JNI]
[Dynamic-linking native method java.io.FileInputStream.open ... JNI]
[Dynamic-linking native method java.io.FileInputStream.readBytes ... JNI] [Dynamic-linking native method java.io.FileInputStream.available ... JNI] [Dynamic-linking native method java.lang.reflect.Array.newArray ... JNI]
[Dynamic-linking native method java.io.FileInputStream.close0 ... JNI]
[Dynamic-linking native method java.io.UnixFileSystem.list ... JNI]
[Dynamic-linking native method java.lang.ClassLoader.findLoadedClass0 ... JNI] [Dynamic-linking native method java.lang.ClassLoader.findBootstrapClass ... JNI]
[Dynamic-linking native method
java.security.AccessController.doPrivileged ... JNI]
[Dynamic-linking native method java.io.UnixFileSystem.getLength ... JNI]
[Dynamic-linking native method sun.misc.Perf.registerNatives ... JNI]
[Registering JNI native method sun.misc.Perf.attach]
[Registering JNI native method sun.misc.Perf.detach]
[Registering JNI native method sun.misc.Perf.createLong]
[Registering JNI native method sun.misc.Perf.createByteArray]
[Registering JNI native method sun.misc.Perf.highResCounter]
[Registering JNI native method sun.misc.Perf.highResFrequency]
[Dynamic-linking native method java.lang.ClassLoader.defineClass1 ... JNI] [Dynamic-linking native method java.lang.ClassLoader.resolveClass0 ... JNI]
[Registering JNI native method Test.testNative]
[Dynamic-linking native method java.io.FileOutputStream.writeBytes ... JNI]
My class loader is sun.misc.launcher$appclassloa...@b92d342
Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.testNative()V [Dynamic-linking native method java.lang.Throwable.getStackTraceDepth ... JNI] [Dynamic-linking native method java.lang.Throwable.getStackTraceElement ... JNI]
        at Test.testNative(Native Method)
        at Test.<init>(Test.java:4)

Martin

On Wed, Jun 9, 2010 at 03:25, David Holmes <david.hol...@oracle.com> wrote:
Hi Martin,

Turns out this is a known (and old) issue (thanks Yuri!):

CR 6493522 "JNI_RegisterNatives fails to bind a method of an uninitialized
class"

I'll add this email info to the CR.

David

Martin Buchholz said the following on 06/09/10 09:12:

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


Reply via email to