我创建了一个 C++ 模块来构建共享库文件,然后使用 JNI 从 Java 调用它。
我有 2 个环境,Windows 和 Unix,我有一个 C++ 可执行程序和一个 Java 程序,我只是为每个环境重新编译。
- 当我在 Unix 中编译我的 tester.exe 程序并使用我的库 (.so) 中的方法运行它时,它工作正常。
- 当我在 Unix 中编译我的 Java 程序并使用 Java 的 loadLibrary 加载我的库 (.so) 时,它工作正常。
当我在 Windows 中编译我的 tester.exe 程序并使用我的库 (.dll) 中的方法运行它时,它工作正常。就像unix版本一样。
当我在 Windows 中编译我的 Java 程序并使用 Java 的 loadLibrary 加载我的库 (.dll) 时,它失败了。它说尝试访问无效地址。
我不知道为什么它在 Windows 中运行时不能与 Java loadLibrary 一起使用,但它在其他任何地方都可以使用相同的代码。如果我延迟加载我的库使用的依赖 DLL,那么我的库会以 Java 加载但无法正常工作。我知道有特定的代码会导致 Java 加载我的库时出现问题,但我无法弄清楚为什么我的 C++ exe 对相同的方法和库没有问题。
我的 dll 有 1 个公开的方法,它从一些现有的库中调用 4 个方法。如果我将这 4 个方法注释掉,那么我的 dll 可以很好地加载到 Java 中。我知道这与我的 dll 链接到的库中的这些方法有关。Java如何看待依赖库有什么不同吗?我已经尝试先加载依赖库,但是我加载的其中一个 dll 文件会导致递归错误并且堆栈溢出。
任何人都知道解决导致递归错误堆栈溢出的 DLL 的方法吗?我需要其中的方法,但我无法使用 java loadLibrary 加载它。
这是有关所涉及文件和实际错误消息的更多详细信息。我在我的初始 dll 文件中添加了一个 DllMain 只是为了查看加载的内容和时间。如果我将同一个程序(my_plain_dll_to_call_JNI_DLL)编译为 exe 文件,一切正常。如果我编译它并从我的 java 程序中加载它,就会发生这种情况。
- myJavaProgram,只需调用 System.loadLibrary() 来加载一个基本的 .dll 文件,该文件调用我的另一个包含 JNI 代码的 dll 中的方法。
- my_plain_dll_to_call_JNI_DLL 是我通过将其链接到我的 dll 库文件创建的一个 dll,只是为了测试依赖关系。它只是从另一个调用我需要的本机代码的 dll 中调用一个方法。
- my_JNI_DLL.ll 是一个 dll 文件,它与我需要从 JNI 访问的现有 C++ 编程库链接。它包含对现有源代码库中方法的直接调用。
我写了文件名,在每行左侧显示文本,以显示执行所在的层。
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 可以访问的内存区域有关,而不是 windows 允许 exe 访问的内存区域。exe 与 SmartHeap 库没有问题,但 Java 不允许我使用它。有任何想法或经验来处理这个问题吗?我试图通过重新编译其他库来删除链接库,但是代码中的正常调用失败了,这通常是有效的。
找到的附加信息 dll 中无法在 java 中加载的函数称为 MemRegisterTask。它来自 Microquill 的名为 SmartHeap 的产品。这是我找到的有关此功能的文档。我认为这种内存分配是导致 java 无法加载它的原因。
MemRegisterTask 初始化 SmartHeap 库。在大多数平台上,您不需要调用 MemRegisterTask,因为 SmartHeap 会在您第一次调用时自行初始化。
SmartHeap 为每个任务或进程维护一个注册引用计数。每次调用 MemRegisterTask 时,此引用计数都会增加。如果您对 SmartHeap 的最后一次调用发生在您的应用程序准备好终止之前,您可以调用 MemUnregisterTask 来终止 SmartHeap。MemUnregisterTask 将注册引用计数减一——当计数为零时,SmartHeap 将释放任何 SmartHeap 分配的内存和与当前任务或进程相关的调试状态。