我创建了一个C++模块,将其构建为共享库文件,然后使用JNI从Java中调用它。
我有两个环境,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++可执行程序在使用相同的方法和库时没有问题。
我的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 中的方法,该 dll 调用了我需要的本地代码。
- my_JNI_DLL.ll 是一个与现有 C++ 编程库链接的 dll 文件,我需要从 JNI 中访问它。它包含直接调用现有源代码库中方法的内容。
我在每行文本左侧写上了显示文件名,以显示执行所处的层次结构。
c:\java myJavaProgram
myJavaProgram: Java静态方法进入。
myJavaProgram: Java调用System.loadLibrary(my_plain_dll_to_call_JNI_DLL)
my_JNI_DLL.dll: 进入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加载成功!
myJavaProgram: Java静态方法退出。
myJavaProgram: 进入Main()。
my_plain_dll_to_call_JNI_DLL: 在call_my_JNI_DLL_method中
my_JNI_DLL.dll: 在my_JNI_DLL_method中
my_JNI_DLL.dll: 进入my_JNI_DLL_CheckEnvironmentVariables()
my_JNI_DLL.dll: 退出my_JNI_DLL_CheckEnvironmentVariables
my_JNI_DLL.dll: 调用StartExistingNativeCode。
#
# Java运行时环境检测到致命错误:
#
# 内部错误(0xc0fb007e),pid=7500,tid=7552
#
# JRE版本:6.0_21-b06
# Java VM:Java HotSpot(TM) Client VM (17.0-b16 mixed mode, sharing windows-x86)
# 问题所在帧:
# C [KERNELBASE.dll+0x9673]
#
# 保存有关更多信息的错误报告文件如下:
# C:\hs_err_pid7500.log
#
# 如果您想提交错误报告,请访问:
# http://java.sun.com/webapps/bugreport/crash.jsp
# 此崩溃发生在Java虚拟机之外的本地代码中。
# 请参见有问题的帧以报告错误。
#
my_plain_dll_to_call_JNI_DLL: DLL_PROCESS_DETACH
my_JNI_DLL.dll: 进入DllMain
my_JNI_DLL.dll DLL_PROCESS_DETACH
更新 我已经将问题缩小到一个内存管理库,它是从另一个dll链接进来的,而我的程序使用了这个dll。它使用的dll是sh33w32.dll,它被称为SmartHeap,由一家名为Microquil的公司开发。我有3.3版本,当Java LoadLibrary试图加载该dll时,它失败了。我不确定我能做什么来让java处理加载该库。它肯定与Java可以访问的内存区域有关,与Windows允许exe访问的不同。exe没有SmartHeap库的问题,但是Java不允许我使用它。有什么想法或经验来处理这个问题吗?我尝试通过重新编译其他库来删除链接库,但是代码中的常规调用失败了。
额外找到的信息 在Java中无法加载的dll中的函数名为MemRegisterTask。它是一个名为SmartHeap的产品的一部分,由Microquill开发。以下是我发现的关于此函数的文档。我认为这种内存分配导致了Java无法加载它。
MemRegisterTask初始化SmartHeap Library。在大多数平台上,您不需要调用MemRegisterTask,因为SmartHeap会在您进行第一次调用时自行初始化。
SmartHeap为每个任务或进程维护注册引用计数。每次调用MemRegisterTask时,此引用计数都会递增。如果您最后一次调用SmartHeap发生在应用程序准备终止之前,您可以调用MemUnregisterTask来终止SmartHeap。MemUnregisterTask将引用计数减少1个——当计数为零时,SmartHeap将释放与当前任务或进程相关联的任何SmartHeap分配的内存和调试状态。