使用包含名为“Node”的类的JNI C ++代码,JVM无法正常工作

时间:2012-03-09 00:39:45

标签: java c++ java-native-interface

我自己和一些队友一直无法理解为什么在使用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的使用有关。当“节点”类获得更多功能时(例如更多属性,额外的虚拟和非虚拟操作),我们可能导致分段错误,而不仅仅是错误的输出。我们正在使用g ++将cpp代码编译成RedHat linux 64位共享库,并使用64位服务器VM运行java代码。请注意,在JVM 1.6u20到1.6u22上,这会产生预期的输出。我没有尝试任何早期版本。

我们决定在这个问题上给予赏金!这里有关于我们已经知道的更多信息:

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

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

5 个答案:

答案 0 :(得分:7)

好的,这不是一个完美的答案,但如果我们没有更好的东西,以下可能会有所帮助。正如其他评论中所解释的那样,问题的关键在于两个不同的C ++类,它们在全局命名空间中命名为Node,一个来自OpenJDK或SunJDK 1.6u23,而在RedHat Linux上(至少)和另一个来自另一个库,两者都需要与其他库共享其符号。要在JDK之前加载符号,我们可以通过调用eg。来设置LD_PRELOAD环境变量:

LD_PRELOAD=libTheNodeTester.so java ...

但是这可能会导致JDK崩溃,如果它真的开始使用我们的符号,就好像它是来自它的库......

答案 1 :(得分:6)

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

答案 2 :(得分:2)

  

似乎JVM错误地构造了“Node”对象   在JNI内

要清楚。 JVM根本不构造“Node”对象。 C ++运行时系统就是这样做的。

我在JNI中使用了大量的C ++,除了我造成的问题之外没有任何问题。

首先想到的是你没有检查'new'运算符的结果是否为null。这不会影响非虚函数,它只会看到你没有使用的'this'为null,但它会阻止调度虚函数,因为通过vtable的间接会出现seg-fault。

为什么它将为null是另一个问题......

答案 3 :(得分:1)

对于踢,请在退出本机代码之前尝试刷新stdout和stderr。我想也许JVM正在退出一些输出缓冲区中的数据。

答案 4 :(得分:0)

您可以在本机代码中添加包装层吗?即编写一个C ++类来代理Node类并从java调用它而不是直接调用Node。

在包装器中,您可以命名导入以避免歧义(例如http://www.glenmccl.com/ns_comp.htm)。