【问题标题】:Calling JNI_CreateJavaVM crashes the program调用 JNI_CreateJavaVM 会使程序崩溃
【发布时间】:2013-01-04 03:37:40
【问题描述】:

我有一个 C DLL,它使用 JNI 代理对执行实际举重的底层 java 程序的任何调用。我正在动态加载 jrockit jvm.dll 以进行函数调用。

供应商 A 有一个 C# DLL,它实际上调用了我的 C DLL,而另一个供应商 B 有一个 C# 程序,它调用了供应商 A 的 C# DLL。

使用供应商 A 的 C# DLL 进行测试时没有任何问题,但在与供应商 B 的 C# 程序集成后,我通过 JNI_CreateJavaVM 初始化 JVM 的调用会导致整个程序崩溃。

任何帮助将不胜感激。

我收到的错误消息是:

[ERROR] Could not find allocated thread local data key in TIB
[ERROR] Could not create fast TLD
JRockit aborted: Unspecified Error(52)
Assertion failed: Could not create fast tld 
In vmDebug Before Abort() (src/jvm/runtime/debug/debug.c:103)

编辑 1: 好的,我已经反汇编了 jvm.dll,它在调用 TlsAlloc 后跟 TLSSet 并到达显示错误消息的代码,第二个图像中 je SHORT 04755D4B 之前的 cmp esi、edx 不能相等。

第一张图片中调用04755DD0的内容在第二张图片中。

有谁知道之前的计算(操作 esi 和 edx 的 1)是做什么的?

编辑 2:(回应 P.T.) 我没有设置任何特定的线程系统,所以我认为它使用的是本机的默认线程系统,如下所示:http://docs.oracle.com/cd/E13222_01/wls/docs81b/jrockit/threads.html

您的猜测很可能是正确的,在查看反汇编时,我发现代码逻辑是这样的,它首先调用 TlsAlloc,然后调用 TlsSetValue 将 TlsAlloc 返回的索引处的线程本地存储设置为常量幻数 4711 之后,它使用 eip 从线程信息块的开头循环查找值 4711,一旦找到它,代码然后再次调用 TlsSetValue 将值设置为 1147,此时它检查 eip 是否为通过确保 [eip] 设置为 1147 来实际指向线程本地存储。

供应商 B 在他们的程序中使用 C#,因此,他们将使用 CLR 虚拟机。一旦它到达供应商 B 调用我的 DLL 的点,他们将已经初始化 WPF prism 和 mef 框架,将所有接口模块加载到各自的位置,初始化所有单例(WPF prism 术语中的导出)模型并初始化 MS 工作流。但是,当我将初始化代码移至前几行时,jvm 初始化成功(这不是初始化 jvm 的正确位置,我们尚未测试其余代码是否有效)。

代码只在 TlsSetValue 失败时分支到错误,TlsSetValue 失败有什么原因吗?在供应商 B 的代码中我应该注意哪些可能导致问题的地方?

【问题讨论】:

  • 神圣的决斗虚拟机,蝙蝠侠!写一个网络服务!或者使用管道。或者用 C 重写你的 Java 东西。恕我直言......
  • 这样做的原因是为了保持与以前版本的程序的向后兼容性
  • 所有代码、C#、C 或 java 都是为 32 位环境编写的,顺便说一句,任何人都不知道什么是 TLD、TIB(谷歌说它可能是线程信息块或类型信息块)或本地数据键?
  • 显然在这种情况下意味着线程信息块。 JNI 的诀窍是始终确保它在没有 Java 部分的情况下工作。不知何故。
  • 您在 jrockit 中使用的是哪个线程系统? (原生还是精简?)TIB 和 TLD 的首字母缩略词是 jrockit 特有的,因此不太可能在任何地方都有词汇表。我的猜测(!)是 jrockit 无法设置它需要识别其线程的线程本地数据。供应商 B 是否在使用线程或虚拟机做任何有趣的事情(例如使用 jrockit 虚拟机?)?

标签: java assembly jvm java-native-interface jrockit


【解决方案1】:

我遇到了同样的错误,我设法弄清楚发生了什么,至少在我的情况下。它看起来像 JRockit 中的一个错误,您的问题对调查它非常有帮助。

对放置在插槽中的“幻数”执行的搜索从 TEB 的开头开始延伸到两页数据。然而,TEB 内部只有 64 个存储槽。见http://msdn.microsoft.com/en-gb/library/windows/desktop/ms686708(v=vs.85).aspx

如果分配的存储槽的索引为 64 或更高,Windows 不会将数据放入嵌入式数组中,而是将其放入 TlsExpansionSlots 指针指向的块中。由于这在 TEB 之外,因此对幻数的搜索会失败,并且 JRockit 会产生此错误。

我的这种情况也发生在 .NET 程序中。我的猜测是 CLR 大量使用 TLS,从而更有可能分配高插槽号。

在我的例子中,JRockit 在尝试写入日志行时实际上崩溃了,可能是因为它发生得太早以至于尚未创建日志。不确定您使用的是哪个版本的 JRockit。我的是:

C:\>java -version
java version "1.6.0_14"
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
BEA JRockit(R) (build R27.6.5-32_o-121899-1.6.0_14-20091001-2107-windows-ia32, compiled mode)

我不知道这是否在以后的版本中修复。如果不是,我们(即我的雇主)可能不得不向 Oracle 提出。

【讨论】:

  • 我尝试在初始化 JVM 之前分配 64 个 tls 插槽,但我设法复制了我这边的错误,看起来就像你说的 JVM 的错误。
  • 我用oracle网站上最新的jrockit-jre1.6.0_37-R28.2.5试了一下,问题依旧
  • 感谢您的检查。我将通过 Oracle 获得一张票。
  • 在与 Oracle 反复数月后,他们决定“不支持”。看起来不会有修复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-26
  • 1970-01-01
  • 2016-02-22
相关资源
最近更新 更多