jni loadLibrary exception - landon30/Bulls GitHub Wiki
- 报错如下
java.lang.UnsatisfiedLinkError: com.naef.jnlua.LuaState.lua_version()Ljava/lang/String;
at com.naef.jnlua.LuaState.lua_version(Native Method)
at com.naef.jnlua.LuaState.<clinit>(LuaState.java:116)
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1941)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1857)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
- 问题分析1
- System.loadLibrary("jnlua5.1") 这种方式的执行过程
- Runtime.getRuntime().loadLibrary0
- ClassLoader.loadLibrary
- ClassLoader#loadLibrary0
- NativeLibrary lib = new NativeLibrary
- 调用本地方法:native void load
- jnlua源代码c#jnlua.c#JNI_OnLoad函数
- 从这里可以看到,referenceclass(env, "com/naef/jnlua/LuaState")),即load#com.naef.jnlua.LuaState并实例化
- 这里要实例化LuaState(第一次调用相关),所以需要调用LuaState的静态初始化块代码
- 注:(*env)->GetFieldID(env, luastate_class, "luaState", "J")
- 这里表示得到一个实例的域的ID,如LuaState#luaState,J表示Long
- 注意1.这里onLoad方法还没有调用完成,即library还没有处理完
- 注意2.静态初始化代码先执行NativeSupport.getInstance().getLoader().load,最终也会调用System.loadLibrary("jnlua5.1"),这里库加载代码的逻辑是之前因为加载过一次(nativeLibraryContext#NativeLibrary),会直接返回;
- 注意3.接着执行初始化代码的第2L代码:LUA_VERSION = lua_version(),这个要调用native方法,但此时library还没有load完毕,所以直接抛出异常:java.lang.UnsatisfiedLinkError
- System.loadLibrary("jnlua5.1") 这种方式的执行过程
- 问题分析2
- 为什么直接调用LuaState.LUA_VERSION就不会抛出异常呢?
- 这个也首先要调用LuaState的静态初始化代码
- 先执行System.loadLibrary("jnlua5.1"),和上面一样,调用NativeLibrary#native load,其只是实例化一个LuaState对象(注:不要认为静态初始化块会再次执行,其只会执行一次,只不过此时还没有执行完毕),实例化完对象后,load结束.
- 执行静态初始化块代码的第二行lua_version(),因为load已结束,所以本地方法顺利完成调用.
- 静态初始化代码块执行完毕
- 静态初始化代码只初始化一次,其内容可以包括如实例化一个对象.
- 实例化对象逻辑和静态初始化代码没有一毛钱关系.
- 唯一的关系是如果是第一次实例化对象(即对象所属的类还没有加载)则会执行静态初始化块代码,而静态初始化块的代码块可以是任何代码(如实例化一个自己的类对象)
- static代码块中的实例化对象不会再触发static代码块的初始化,其只是static代码块初始化的一行代码而已,因为static块初始化的执行肯定在之前已经被其他方式调用而执行了.
- 如调用类的静态方法
- 第一次在外部实例化对象(其实static内部的实例化相当于第二次实例化了)
- ClassLoader#load等
- 总结:
- System.loadLibrary("jnlua5.1"),直接尝试加载jnlua5.1本地库会抛出异常,具体原因在上面
- 需要保证library load成功之后才调用相关的native方法,否则会抛出异常
JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM *vm, void *reserved) {
JNIEnv *env;
/* Get environment */
if ((*vm)->GetEnv(vm, (void **) &env, JNLUA_JNIVERSION) != JNI_OK) {
return JNLUA_JNIVERSION;
}
/* Lookup and pin classes, fields and methods */
if (!(luastate_class = referenceclass(env, "com/naef/jnlua/LuaState"))
|| !(luastate_id = (*env)->GetFieldID(env, luastate_class, "luaState", "J"))
|| !(luathread_id = (*env)->GetFieldID(env, luastate_class, "luaThread", "J"))) {
return JNLUA_JNIVERSION;
}
...
static {
NativeSupport.getInstance().getLoader().load();
LUA_VERSION = lua_version();
}