java调用本地方法必须通过jni,这是唯一标准机制;需用native关键字声明方法并在静态块中调用system.loadlibrary()加载库,注意库名省略前缀后缀,函数签名须严格遵循jni规则,参数和对象操作必须通过jnienv api安全处理。

Java 调用本地方法(Native Method)必须通过 JNI(Java Native Interface),这不是可选的“优化手段”,而是唯一标准机制。绕过 JNI 直接调用 C/C++ 函数在 JVM 中不可能实现。
如何声明并加载 native 方法
Java 端需用 native 关键字修饰方法,并在类静态块中调用 System.loadLibrary() 加载动态库。注意:库名不带前缀和后缀(如 Linux 的 libhello.so,只传 "hello");Windows 下 hello.dll 同样传 "hello"。
常见错误包括:
-
UnsatisfiedLinkError:库未找到、路径不对、函数签名不匹配、架构不一致(如 JVM 是 64 位,却加载了 32 位 so/dll) - 静态块执行早于 native 方法调用,但
loadLibrary失败时不会抛异常,只会静默失败——建议加try-catch包裹并打印日志 - 多个类共用同一库时,重复
loadLibrary无害,但load(带绝对路径)重复调用会报错
如何生成正确的 JNI 函数签名
JNI 要求 native 函数名严格遵循 Java_<full_classname>_<methodname></methodname></full_classname> 规则,其中包名中的 . 替换为 _,且若有下划线或特殊字符需用 _1 转义(如方法名是 get-url → get_1url)。手动拼写极易出错。
立即学习“Java免费学习笔记(深入)”;
推荐做法:
- 先写好 Java 类,含
native方法和static块 - 用
javac编译后,运行javah -jni <classname></classname>(JDK 8 及以前)或javac -h . <java_file></java_file>(JDK 9+,头文件输出到当前目录) - 直接按生成的
.h文件里声明的函数原型实现 C 函数,不要手写
例如 com.example.NativeUtil.add(int, int) 生成的函数名是 Java_com_example_NativeUtil_add,参数列表还包含 JNIEnv* 和 jclass 或 jobject。
本书是全面讲述PHP与MySQL的经典之作,书中不但全面介绍了两种技术的核心特性,还讲解了如何高效地结合这两种技术构建健壮的数据驱动的应用程序。本书涵盖了两种技术新版本中出现的最新特性,书中大量实际的示例和深入的分析均来自于作者在这方面多年的专业经验,可用于解决开发者在实际中所面临的各种挑战。
如何安全传递和使用 Java 对象参数
JNI 层不能直接操作 Java 对象字段或调用方法,必须通过 JNIEnv 提供的 API。比如读取 String 要用 GetStringUTFChars() 或 GetStringUTFRegion(),用完必须配对调用 ReleaseStringUTFChars(),否则内存泄漏。
关键注意事项:
-
jstring不是 C 字符串,const char*是临时指针,不可长期保存或跨线程使用 - 数组(
jintArray等)需用GetIntArrayElements()+ReleaseIntArrayElements(),或更安全的GetPrimitiveArrayCritical()(但会暂停 GC,必须快速释放) - 回调 Java 方法要用
GetMethodID+CallVoidMethod等,注意对象引用是否为NULL,避免 crash - 全局引用(
NewGlobalRef)用于跨 JNI 调用生命周期持有对象,但必须手动DeleteGlobalRef,否则内存泄漏
常见崩溃与调试切入点
JNI 错误常导致 JVM 直接 crash(SIGSEGV / EXCEPTION_ACCESS_VIOLATION),而非抛 Java 异常。这类问题极难定位。
优先检查:
- 是否在非 JNI 线程(如 pthread 创建的线程)中直接用了
JNIEnv*?必须先调用AttachCurrentThread - 是否对已释放的局部引用(
jobject)再次使用?局部引用仅在当前 native 方法内有效 - 是否在 native 代码中抛了 C++ 异常却没捕获?JVM 不处理 C++ exception,会直接终止
- 是否误把
jobject当作jclass传给FindClass?后者只接受类名字符串
调试建议:Linux 下用 gdb --args java ... 启动,crash 后用 bt 看栈;Windows 下用 WinDbg 或 VS 附加进程;开启 JVM 参数 -Xcheck:jni 可捕获部分非法 JNI 调用(但有性能开销)。
JNI 的本质是 JVM 与 native 代码之间的契约接口,任何一处签名、生命周期或线程模型的偏差,都可能引发静默失败或瞬间崩溃。它不难写,但极难写对——真正棘手的从来不是“怎么调”,而是“什么时候不该调”以及“调完怎么收尾”。









