Tom*_*Tom 24 c++ java 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;
}
}
Run Code Online (Sandbox Code Playgroud)
NodeTester.java:
public class NodeTester {
public static native void testNode ();
static {
System.loadLibrary("nodetester");
}
public static final void main (String[] args) {
NodeTester.testNode();
}
}
Run Code Online (Sandbox Code Playgroud)
预期产量:
11
Run Code Online (Sandbox Code Playgroud)
JVM 1.6u23到1.6u31的实际输出:
1
Run Code Online (Sandbox Code Playgroud)
似乎JVM在JNI中错误地构造了"Node"对象; 虽然这段代码可能与JNI的使用有关.当"Node"类获得更多功能时(例如更多属性,额外的虚拟和非虚拟操作),我们可能会导致分段错误,而不仅仅是错误的输出.我们正在使用g ++将cpp代码编译成RedHat linux 64位共享库,并使用64位服务器VM运行java代码.请注意,在JVM 1.6u20到1.6u22上,这会产生预期的输出.我没有尝试任何早期版本.
我们决定在这个问题上给予赏金!这里有关于我们已经知道的更多信息:
不幸的是对于我们来说,没有这些项目导致可行的解决方案 - "大问题"我提到的是,我们正在处理的一个大的,现有的代码库与名为"节点" C++类,这是我们需要通过JNI访问.我们还尝试了几个g ++和javac编译器选项,以及几个JVM选项,但无济于事(尽管如果有人偶然发现实际产生预期结果的那个,这将是一个可接受的解决方案).
好吧,这不是一个完美的答案,但如果我们没有更好的东西,以下可能会有所帮助.正如其他评论中所解释的那样,问题的关键在于两个不同的C++类,它们都Node在全局命名空间中命名,一个来自OpenJDK或SunJDK 1.6u23,一个来自RedHat Linux(至少),另一个来自另一个库,两者都是其中需要与其他库共享其符号.为了在JDK之前加载我们的符号,我们可以LD_PRELOAD通过调用eg 来设置环境变量:
LD_PRELOAD=libTheNodeTester.so java ...
Run Code Online (Sandbox Code Playgroud)
但是这可能会导致JDK崩溃,如果它实际上开始使用我们的符号,就好像它是来自它的库...
通过查看HotSpot代码,有一个node.hpp/node.cpp声明了一个没有命名空间的节点类.
也许与纯虚函数存在冲突.
我没有足够的VM知识来进一步挖掘......