共有ライブラリ ファイルにビルドする C++ モジュールを作成し、JNI を使用して Java から呼び出します。
Windows と Unix の 2 つの環境があり、C++ 実行可能プログラムと、各環境用に再コンパイルする Java プログラムがあります。
- Unix で tester.exe プログラムをコンパイルし、ライブラリ (.so) のメソッドを使用して実行すると、正常に動作します。
- Java プログラムを Unix でコンパイルし、ライブラリ (.so) を Java の loadLibrary でロードすると、正常に動作します。
Windows で tester.exe プログラムをコンパイルし、ライブラリ (.dll) のメソッドを使用して実行すると、正常に動作します。UNIX版と同じです。
Windows で Java プログラムをコンパイルし、Java の loadLibrary を使用してライブラリ (.dll) をロードすると、失敗します。無効なアドレスにアクセスしようとしていますと表示されます。
Windows で実行しているときに Java loadLibrary で動作しない理由がわかりませんが、同じコードを使用する他の場所では動作します。ライブラリが使用する依存 DLL の読み込みを遅らせると、ライブラリは Java で読み込まれますが、機能しません。Java がライブラリをロードする際に問題を引き起こす特定のコードがあることは知っていますが、C++ exe が同じメソッドとライブラリで問題を起こさない理由がわかりません。
私のdllには、いくつかの既存のライブラリから4つのメソッドを呼び出す公開メソッドが1つあります。これら 4 つのメソッドをコメントアウトすると、dll が Java で正常に読み込まれます。私のdllがリンクしているライブラリのこれらのメソッドと関係があることはわかっています。Java が依存ライブラリを認識する方法に違いはありますか? 最初に依存ライブラリをロードしようとしましたが、ロードした dll ファイルの 1 つが再帰エラーを引き起こし、スタック オーバーフローが発生します。
再帰エラーからスタック オーバーフローを引き起こす DLL を回避する方法を知っている人はいますか? その中にメソッドが必要ですが、Java loadLibrary でロードできません。
関連するファイルと実際のエラー メッセージの詳細を次に示します。何をいつロードするかを確認するためだけに、最初の dll ファイルに DllMain を追加しました。同じプログラム (my_plain_dll_to_call_JNI_DLL) を exe ファイルとしてコンパイルすると、すべて正常に動作します。コンパイルしてJavaプログラムからロードすると、これが発生します。
- myJavaProgram は、単純に System.loadLibrary() を呼び出して、JNI コードを含む他の dll のメソッドを呼び出す基本的な .dll ファイルをロードします。
- my_plain_dll_to_call_JNI_DLL は、依存関係をテストするためだけに dll ライブラリ ファイルにリンクして作成した dll です。必要なネイティブコードを呼び出している他のdllからメソッドを呼び出すだけです。
- my_JNI_DLL.ll は、JNI からアクセスする必要がある既存の C++ プログラミング ライブラリにリンクされた dll ファイルです。これには、既存のソース コード ライブラリ内のメソッドへの直接呼び出しが含まれています。
実行がどのレイヤーにあるかを示すために、各行の左側にテキストを表示するファイル名を書きました。
c:\java myJavaProgram
myJavaProgram: Java Static Method Entry.
myJavaProgram: Java Calling System.loadLibrary(my_plain_dll_to_call_JNI_DLL)
my_JNI_DLL.dll: Entering DllMain
my_JNI_DLL.dll: DLL_PROCESS_ATTACH
my_plain_dll_to_call_JNI_DLL: DLL_PROCESS_ATTACH
my_plain_dll_to_call_JNI_DLL: DLL_THREAD_ATTACH
my_plain_dll_to_call_JNI_DLL: DLL_THREAD_DETACH
my_plain_dll_to_call_JNI_DLL: DLL_PROCESS_DETACH
myJavaProgram: my_plain_dll_to_call_JNI_DLL Loaded!
myJavaProgram: Java Static Method Exit.
myJavaProgram: Entering Main().
my_plain_dll_to_call_JNI_DLL: In call_my_JNI_DLL_method
my_JNI_DLL.dll: In my_JNI_DLL_method
my_JNI_DLL.dll: Entering my_JNI_DLL_CheckEnvironmentVariables()
my_JNI_DLL.dll: Exiting my_JNI_DLL_CheckEnvironmentVariables
my_JNI_DLL.dll: Calling StartExistingNativeCode.
#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (0xc0fb007e), pid=7500, tid=7552
#
# JRE version: 6.0_21-b06
# Java VM: Java HotSpot(TM) Client VM (17.0-b16 mixed mode, sharing windows-x86 )
# Problematic frame:
# C [KERNELBASE.dll+0x9673]
#
# An error report file with more information is saved as:
# C:\hs_err_pid7500.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
my_plain_dll_to_call_JNI_DLL: DLL_PROCESS_DETACH
my_JNI_DLL.dll: Entering DllMain
my_JNI_DLL.dll DLL_PROCESS_DETACH
更新 プログラムが使用する別の dll からリンクされているメモリ管理ライブラリに問題を絞り込みました。それが使用する dll は sh33w32.dll で、SmartHeap と呼ばれ、Microquil という名前の会社によるものだと思います。バージョン 3.3 を使用していますが、Java LoadLibrary がその dll をロードしようとすると失敗します。そのライブラリをJavaハンドルでロードするために何ができるかわかりません。Javaがアクセスできるメモリ領域と、exeがアクセスできるウィンドウに関係がある必要があります。exe は SmartHeap ライブラリに問題はありませんが、Java では使用できません。これに対処するアイデアや経験はありますか?他のライブラリを再コンパイルして、リンクされたライブラリを削除しようとしましたが、通常は機能するコード内の通常の呼び出しが失敗します。
見つかった追加情報 Java でのロードに失敗する dll 内の関数は、MemRegisterTask と呼ばれます。これは、Microquill の SmartHeap という製品からのものです。これは、この関数について見つけたドキュメントです。このメモリ割り当てが、Java のロードに失敗する原因だと思います。
MemRegisterTask は SmartHeap ライブラリを初期化します。ほとんどのプラットフォームでは、MemRegisterTask を呼び出す必要はありません。これは、SmartHeap が最初の呼び出しを行ったときに自身を初期化するためです。
SmartHeap は、各タスクまたはプロセスの登録参照カウントを維持します。MemRegisterTask を呼び出すたびに、この参照カウントがインクリメントされます。アプリケーションを終了する準備が整う前に SmartHeap への最後の呼び出しが発生した場合は、MemUnregisterTask を呼び出して SmartHeap を終了できます。MemUnregisterTask は、登録参照カウントを 1 減らします。カウントがゼロの場合、SmartHeap は、現在のタスクまたはプロセスに関連付けられている SmartHeap に割り当てられたメモリとデバッグ状態を解放します。