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