根本原因是JVM只在java.library.path等指定路径中查找libcalc.so等命名规范的文件,而非当前目录或resources;且需确保JNI函数名、头文件、ABI架构严格匹配。

System.loadLibrary 为什么找不到 so 文件
根本原因不是 Java 代码写错了,而是 JVM 没在 java.library.path 指定的路径里找到对应命名的 so。比如你写了 System.loadLibrary("calc"),JVM 实际会去找 libcalc.so(Linux)或 calc.dll(Windows),且只查以下三类路径:
-
java.library.path中列出的目录(启动时用-Djava.library.path=...指定) -
sun.boot.library.path(通常是 JVM 自带的 native 库路径,如$JAVA_HOME/jre/lib/amd64) - 当前 classloader 的
findLibrary返回路径(极少用于自定义 so)
常见错误:把 libcalc.so 放在项目根目录或 src/main/resources 下——这些路径默认不在搜索链中。正确做法是启动时加参数:java -Djava.library.path=/path/to/so/dir MyApp,或者改环境变量 LD_LIBRARY_PATH(Linux/macOS)。
JNI 函数名必须严格匹配 Java 类全限定名
Java 声明 public static native int add(int a, int b); 在 com.example.Calc 类里,C 端函数名就必须是:Java_com_example_Calc_add。漏一个下划线、大小写错、包名少一级,都会报 UnsatisfiedLinkError: No native method found。
别手写这个函数名——用 javah(JDK 8 及以前)或 javac -h(JDK 9+)自动生成头文件:
javac Calc.java javac -h . Calc.java
生成的 com_example_Calc.h 里就含标准签名,直接照抄实现即可。注意:javac -h 输出的是目录结构(com/example/Calc.h),不是扁平文件名。
编译 so 时必须包含 jni.h 和 jni_md.h
缺这两个头文件,JNIEnv*、jint、JNIEXPORT 等全报错。它们不在系统默认 include 路径里,必须显式指定:
-
${JAVA_HOME}/include(含jni.h) -
${JAVA_HOME}/include/linux(Linux)或${JAVA_HOME}/include/darwin(macOS)或${JAVA_HOME}/include/win32(Windows)(含jni_md.h)
典型编译命令(Linux):
gcc -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux \
-shared -o libcalc.so calc.c
如果用 CMake,记得在 target_include_directories 里加上这两条路径。OpenJDK 和 Oracle JDK 的头文件位置一致,但某些精简版 JRE 可能不带 include 目录——务必确认 $JAVA_HOME/include/jni.h 存在。
CallStaticIntMethod 这类 JNI 调用容易崩在参数类型上
Java 层调用本地方法是“被动触发”,但反过来——C 代码里用 CallStaticIntMethod 主动调 Java 方法时,参数必须是 JVM 内部对象句柄(jobject、jstring 等),不能传原始指针或 C 字符串。
例如想从 C 侧调 Java 的 log(String msg):
- 错:直接传
"hello"—— 类型不匹配,JVM 解析失败 - 对:先用
env->NewStringUTF("hello")创建jstring,再传给CallStaticVoidMethod
还有个隐藏坑:jstring 是局部引用,用完不调 DeleteLocalRef 会泄漏;频繁调用时得手动管理引用生命周期。OpenJDK 的 jni.cpp 里所有 CallXXXMethod 最终都走 jni_invoke_static,它对参数做严格校验——类型不符直接 abort,不会抛 Java 异常。
最常被忽略的是:so 文件的 ABI 兼容性。x86_64 的 so 不能在 aarch64 的 JVM 上加载,哪怕路径、函数名、头文件全对。运行前先用 file libxxx.so 看架构,确保和 java -version 输出的 JVM 架构一致。











