JVM 无法按预期方式与 JNI C++代码一起使用,其中包含名为“Node”的类

2022-09-01 21:17:48

我和一些队友一直无法理解为什么在使用 JVM 版本 1.6u23 到 1.6u31(截至本文发布的最新版本)时,以下代码片段不会给出正确的输出。此代码片段表示对更大问题的简化:

更新:稍微修改了示例,以将重点放在“virtual_function()”似乎没有被调用的问题上。

更新:根据迄今为止的评论进一步简化了示例。

NodeTester.cpp:

#include <iostream>
#include <jni.h>

class Node {
  public:
    Node () :m_counter(0) {}
    virtual ~Node () {}

    virtual void virtual_function () {
      m_counter += 10;
    }

    void non_virtual_function () {
      m_counter += 1;
    }

    int get_counter () {
      return m_counter;
    }

  private:
    int m_counter;

};

extern "C" {
  JNIEXPORT void JNICALL Java_NodeTester_testNode (JNIEnv *jni_env_rptr, 
                                                   jclass java_class) {
    Node *node_rptr = new Node();
    node_rptr->non_virtual_function();
    node_rptr->virtual_function();

    std::cout << node_rptr->get_counter() << std::endl;

    delete node_rptr;
  }
}

NodeTester.java:

public class NodeTester {
  public static native void testNode ();

  static {
    System.loadLibrary("nodetester");
  }

  public static final void main (String[] args) {
    NodeTester.testNode();
  }
}

预期输出:

11

JVM 1.6u23 至 1.6u31 的实际输出:

1

看起来JVM错误地在JNI中构造了“Node”对象;尽管此代码可能对其 JNI 的使用有一些不正确之处。当类“Node”添加更多功能(例如,更多属性,其他虚拟和非虚拟操作)时,我们可能会导致分段错误,而不仅仅是不正确的输出。我们使用 g++ 将 cpp 代码编译到 RedHat linux 64 位共享对象库中,并使用 64 位 Server VM 运行 java 代码。请注意,在 JVM 1.6u20 到 1.6u22 上,这将生成预期的输出。我没有尝试过任何早期版本。

我们决定在这个问题上放一个赏金!以下是有关我们已经知道的更多信息:

  • JVM 1.6u22(及更早版本)产生预期结果
  • 重命名“Node”或将其放在命名空间中会产生预期的结果
  • 在堆栈上分配“Node”对象而不是在 JNI 函数中的堆上分配“节点”对象会产生预期的结果
  • 类“节点”的非虚拟组件没有问题

不幸的是,这些项目都没有带来可行的解决方案 - 我提到的“更大的问题”是我们正在处理一个大型的现有代码库,其中包含一个名为“Node”的C++类,我们需要通过JNI访问它。我们还尝试了几个g ++和javac编译器选项,以及几个JVM选项,但无济于事(尽管如果有人偶然发现了一个实际产生预期结果的选项,这将是一个可以接受的解决方案)。


答案 1

好吧,这不是一个完美的答案,但是如果我们没有更好的答案,以下可能会有所帮助。正如在其他评论中所解释的那样,问题的症结在于两个不同的C++类,这两个类都在全局命名空间中命名,一个来自OpenJDK或RedHat Linux上的SunJDK 1.6u23及更高版本(至少),另一个来自另一个库,这两个都需要与其他库共享其符号。为了在 JDK 之前加载我们的符号,我们可以设置环境变量,例如:NodeLD_PRELOAD

LD_PRELOAD=libTheNodeTester.so java ...

但是,如果JDK实际上开始使用我们的符号,就好像它是其库中的符号一样,这可能会使JDK崩溃......


答案 2

通过查看 HotSpot 代码,有一个 node.hpp/node.cpp 它声明了一个没有命名空间的 node 类。
也许与纯虚函数有冲突。
我没有足够的VM知识来进一步挖掘...


推荐