jni loadLibrary exception - landon30/Bulls GitHub Wiki

为什么System.loadLibrary("jnlua5.1")会直接抛出异常?

  • 报错如下
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
  • 问题分析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();
	}
⚠️ **GitHub.com Fallback** ⚠️