JNI利用時 can't find dependent libraries となり起動できない
exewrapでEXE化した場合もDLLの読み込み動作に違いはないのでJNIは動作します。
"Can't find dependent libraries" は、指定したライブラリが依存しているライブラリが見つからなかったことを表しています。
System.load()、System.loadLibrary()で直接指定したライブラリを検索するときには java.library.pathが使用されますが、依存ライブラリのロードにはjava.library.pathは使用されません。
依存ライブラリのロードはJavaではなくOSによっておこなわれるからです。
ファイル名が "libtensorflow.so" となっているのもLinux用ライブラリのようで気になります。
Windows用のライブラリ "tensorflow.dll" がありませんか?
System.load()ではなく、System.loadLibrary()を使用すると、 プリフィックスや拡張子を省略してライブラリ名のみを指定できます。
System.loadLibrary("tensorflow") とすると、Linuxなら "libtensorflow.so"、Windowsなら "tensorflow.dll" に展開されます。
System.load()の指定、System.getenv("PATH") の内容、CUDAのインストールパス等を提示いただけないでしょうか?
また、DependenciesGui.exe( https://github.com/lucasg/Dependencies )を使うとライブラリの依存関係を調べることができます。
DependenciesGui.exe を起動して、tensorflow ライブラリをドラッグアンドドロップするとどうなるでしょうか?
参照できない依存ライブラリがないでしょうか?
ご回答ありがとうございます。
ファイル名が "libtensorflow.so" となっているのもLinux用ライブラリのようで気になります。
混乱させてしまい申しわけありません。
.so となっていますが、実体は .dll と同じです。
System.load()の指定、System.getenv("PATH") の内容、CUDAのインストールパス等を提示いただけないでしょうか?
System.load() は以下のように実行しています。
java.class.path からlibのパスを求めて、その配下にある各種dllを読み込んでいます。
- static {
- libs = getLibsDir();
- for (String lib : Arrays.asList("tbb.dll", "opencv_world345.dll", "opencv_java345.dll", "libtensorflow.so")) {
- System.load(new File(libs, lib).getAbsolutePath());
- }
- }
- private static File getLibsDir() {
- for (String path : System.getProperty("java.class.path").split(File.pathSeparator)) {
- if (path.contains("opencv") && path.endsWith(".jar")) {
- return new File(path).getParentFile();
- }
- }
- return new File("lib");
- }
System.getenv("PATH") の結果は以下のURLを参照ください。
exeから起動したときは直下のlibフォルダがPATHに追加されていますが、それ以外の差異はほとんどありません。
CUDAのInstallパスは以下です。
そういえば、dllに対してmklinkにてSymbolicLinkを張っているのですが、何か関係がありますでしょうか?
DependenciesGui.exe を起動して、tensorflow ライブラリをドラッグアンドドロップするとどうなるでしょうか?
依存するdllは全て参照できていると思われます。
以下の画面のキャプチャを参照ください。
java -jar xxx.jar にて直接jarから起動したときは問題ありませんので、参照できないものは無いと考えていますが…。
各種状況資料の取得ありがとうございます。 どのような環境か見えてきましたが、何が原因になっているのかは分かりませんでした。すみません。
そういえば、dllに対してmklinkにてSymbolicLinkを張っているのですが、何か関係がありますでしょうか?
シンボリックリンクは関係ないと思います。 DLLのロードはJavaとは無関係にOSによっておこなわれますので。
切り分けのために、jar実行時とexe実行時のPATHを合わせてみていただけないでしょうか?
1. jar実行前に環境変数PATHに以下の3つのパスを追加しておく。
これでjar実行時とexe実行時のPATHは完全に同じになるはずです。 これでもexeの場合のみライブラリのロードに失敗するのであれば PATH の違いが原因ではないと考えられます。
2. exe作成時のexewrapオプションに -L x を指定する。
exewrapは -L オプション省略時に lib を既定のライブラリパスに設定します。 これが D:\App\lib が PATH に追加される理由になっています。 -L x を指定することで既定のライブラリパスが x に設定されます。 D:\App\x という存在しないパスが PATH に追加されることにはなりますが、D:\App\lib は PATH に含まれなくなります。 これでもexeの場合のみライブラリのロードに失敗するのであれば PATH に D:\App\lib が含まれていることが原因ではないと考えられます。
障害原因が分からず、いろいろと試行錯誤をお願いするばかりで申し訳ないです。。
こちらこそ、ご丁寧に対応いただきありがとうございます。
1. jar実行前に環境変数PATHに以下の3つのパスを追加しておく。
試してみましたが、エラーは変わらずでした。
2. exe作成時のexewrapオプションに -L x を指定する。
こちらに問合せする前に既に試し済みでした。相変わらずエラーです。
jar直接起動時とexe起動時とでClassLoaderが若干違うようなのですが、何か関連はありますでしょうか?
↓↓↓ getClass().getClassLoader() から getParent() で親に遡ったもの
sun.misc.Launcher$AppClassLoader@73d16e93 sun.misc.Launcher$ExtClassLoader@2f336cc7
Loader@5c647e05 sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$ExtClassLoader@239a3454
他にも何か手掛かりがないか、引き続き調べてみます…。
ProcessMonitor を使ってファイルの読み込み状況を追跡してみました。
結果は以下です。
jar -jar xxx.jar により起動したときのProcessMonitor
xxx.exe により起動したときのProcessMonitor
exeから起動したときは、以下の3つのパスだけを検索対象としているようです。
つまり、元の環境変数PATHが無視されているのが原因のようです。
よろしくお願いいたします。
ProcessMonitorを使っての詳細な調査ありがとうございます。
おかで、こちらでも依存DLLのロードに失敗する事象を再現することができました。
まだ、原因の特定には至っていませんが、
手元の環境で事象再現できるようになりましたので徹底的に調査して必ず解決します。
もう少しお時間をください。
依存DLLロードに失敗するバグを修正したバージョンをビルドしました。
下記のバージョンで改善するかどうか確認していただけないでしょうか。
ご連絡遅くなりました。 1.4.2pre1 にて依存dllが読み込めることを確認いたしました。
ただ、CUDAを起動するところで別の java.lang.UnsatisfiedLinkError が発生しております。 exewrap1.2.6でも同様のエラーが出ていますので、もしかすると何か別の問題がまだあるのかもしれません。
Native側で隠蔽されているのか、詳細なエラーが出ておらず現時点でよく分かっていません。 引き続き手掛かりを探ってみますので、少々お時間ください。
JNIを利用したGUIアプリケーションを作成しています。 外部ファイルはSystem.loadを呼び出してロードしているのですが、その際に「can't find dependent libraries」が発生しロードに失敗します。 具体的には libtensorflow.so をロードしており、NVIDIA CUDAのdllを必要とします。
exewrapから作成したexeだと上述のエラーとなるのですが、java -jar XXX.jar の形でjarのまま起動したときはエラーは発生せずに起動に成功します。 jarのままのときとexe化した後で、System.getenv("PATH") や System.getProperty("java.library.path") を出力してみたのですが、特に大きな差異は見られませんでした。
exe化する際には何か特別な手続きが必要なのでしょうか…。