StackTraces
Java已驗證

Java UnsatisfiedLinkError 加載 Native 庫失敗

@debug_master32 天前更新閱讀 6 min00

這個錯本質是「Java 想調 C/C++ 寫的本地方法,結果沒接上頭」。它給出的信息有限——一句 no xxx in java.library.path 或 Library not found——所以第一步要做的是沿「找不到庫 → 找到了但方法/架構不對 → 庫自身依賴缺失」的順序排查,而不是直接上手改程式碼。

Issue & Env(案發現場與環境)

  • 現象:啟動時類初始化加載 native 庫失敗,進程退出;或首次調用 native 方法時拋錯。
  • 環境:使用 JNI 或依賴帶 native 庫的三方包(TIBCO、OpenCV、Netty 的 epoll 等),Windows .dll / Linux .so / macOS .dylib。

StackTrace(堆疊日誌)

最經典的一種:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no HelloNative in java.library.path
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
	at java.lang.Runtime.loadLibrary0(Runtime.java:870)
	at java.lang.System.loadLibrary(System.java:1122)
	at com.example.NativeDemo.<clinit>(NativeDemo.java:9)

也有庫找到了、但依賴的另一個庫缺了:

java.lang.UnsatisfiedLinkError: /path/to/libfoo.so: libbar.so: cannot open shared object file: No such file or directory
    at java.lang.ClassLoader$NativeLibrary.load(Native Method)
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1934)
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1822)
    at java.lang.Runtime.loadLibrary0(Runtime.java:870)
    at java.lang.System.loadLibrary(System.java:1122)
    at com.example.NativeDemo.<clinit>(NativeDemo.java:9)

Reproduce(復現程式碼)

聲明 native 方法並用 System.loadLibrary 加載:

public class NativeDemo {
    public native void sayHello();

    static {
        System.loadLibrary("HelloNative");
    }

    public static void main(String[] args) {
        new NativeDemo().sayHello();
    }
}

若 libHelloNative.so 不在 JVM 能找到的路徑裡,啟動就報上面的錯。

Root Cause(根因分析)

JVM 只從 java.library.path 這個系統屬性(以及 Linux 下的 LD_LIBRARY_PATH)列出的目錄裡找本地庫,庫文件不在這些路徑,就報 no xxx in java.library.path。

搞錯的原因集中在這幾類:

  1. 庫名拼錯或不帶擴展名——System.loadLibrary("HelloNative") 會自動按平臺補成 libHelloNative.so、HelloNative.dll,你手上文件若叫別的名字就對不上。
  2. 庫的架構和 JVM 不匹配——32 位 JVM 加載不了 64 位的 .so,反之亦然。
  3. 庫自身還依賴別的 native 庫,那個依賴沒裝、沒在 LD_LIBRARY_PATH 裡。
  4. 庫裡確實沒有你要調的那個 native 方法——C 側符號名改了,或沒導出。

「把庫拷到某個目錄」但沒加到 java.library.path 是最常見的徒勞操作:JVM 不會去那個任意目錄找,只會去它列表裡的位置。

The Fix(解決方案)

先確認庫到底在哪、JVM 到底去哪找:

System.out.println(System.getProperty("java.library.path"));

然後把庫所在目錄顯式塞進搜索路徑:

java -Djava.library.path=/path/to/lib -jar app.jar
# Linux 也可用环境变量
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH

用 System.load() 傳絕對路徑能繞過路徑查找,但生產程式碼別這麼寫,路徑在不同機器、容器裡都不一樣,會破壞可移植性:

System.load("/path/to/lib/libHelloNative.so"); // 不推荐用于生产

庫名對不上就核對文件的實際命名和 loadLibrary 傳的名字;架構對不上就換匹配 JVM 架構的庫;庫自身的依賴缺了,用 ldd /path/to/libfoo.so(Linux)看哪些依賴 not found,再補裝對應的系統包,Docker 裡要把裝依賴寫進 Dockerfile。最後確認 C 側符號名和 Java 側 native 方法按 JNI 命名規則一致,別在導出符號上改名。

References(參考來源)

最後更新於 2026-09-09

這篇幫到你了嗎?

剛解決了一個棘手的報錯?花兩分鐘記錄下來,幫助下一個遇到同樣問題的開發者。

貢獻一條解法