静态c++;对象并调用一个连续两次使用字符串参数的java函数会导致JVM崩溃
因此,根据我的评论员的要求,我终于找到了一个复制我错误的MCVE。因此,java java java语言使用Java语言调用JNI,并保存一个指向JNENV的指针,它用于调用Java类中的方法(从C++调用的Java类不一定是原始调用java对象,这就是为什么输入的Joobe不用于回调)。在我进一步解释之前,请允许我发布所有代码: JniTest.java静态c++;对象并调用一个连续两次使用字符串参数的java函数会导致JVM崩溃,java,c++,jvm,java-native-interface,Java,C++,Jvm,Java Native Interface,因此,根据我的评论员的要求,我终于找到了一个复制我错误的MCVE。因此,java java java语言使用Java语言调用JNI,并保存一个指向JNENV的指针,它用于调用Java类中的方法(从C++调用的Java类不一定是原始调用java对象,这就是为什么输入的Joobe不用于回调)。在我进一步解释之前,请允许我发布所有代码: JniTest.java package jnitest; public class JniTestJava { public static void main
package jnitest;
public class JniTestJava {
public static void main(String[] args) {
try {
System.load("<path-to-dll>");
} catch (Throwable e) {
e.printStackTrace();
}
DllFunctions dllFunctions = new DllFunctions();
dllFunctions.setup();
dllFunctions.singleIntFunctionCall();
dllFunctions.doubleIntFunctionCall();
dllFunctions.singleStringFunctionCall();
dllFunctions.doubleStringFunctionCall();
}
public void javaStringFunction(String input){
System.out.println(input);
}
public void javaIntFunction(int input){
System.out.println(input);
}
}
package jnitest;
public class DllFunctions{
public native void singleIntFunctionCall();
public native void doubleIntFunctionCall();
public native void singleStringFunctionCall();
public native void doubleStringFunctionCall();
public native void setup();
}
JniTestCpp.h
#include <jni.h>
#ifndef _Included_jnitest_JniTestJava
#define _Included_jnitest_JniTestJava
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_setup(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleIntFunctionCall(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleIntFunctionCall(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleStringFunctionCall(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleStringFunctionCall(JNIEnv* java_env, jobject);
#ifdef __cplusplus
}
#endif
#endif
#include <jni.h>
class JniTestClass {
typedef jint(JNICALL * GetCreatedJavaVMs)(JavaVM**, jsize, jsize*);
public:
void setup();
void callJavaStringFunction();
void callJavaIntFunction();
void throwException(jthrowable ex);
private:
jobject myObject;
jclass myClass;
JNIEnv* env;
};
#include "JniTestCpp.h"
#include "JniTestClass.h"
extern "C"
{
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_setup(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleIntFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaIntFunction();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleIntFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaIntFunction();
jniTestClass.callJavaIntFunction();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleStringFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaStringFunction();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleStringFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaStringFunction();
jniTestClass.callJavaStringFunction();
}
}
JniTestClass.h
#include <jni.h>
#ifndef _Included_jnitest_JniTestJava
#define _Included_jnitest_JniTestJava
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_setup(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleIntFunctionCall(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleIntFunctionCall(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleStringFunctionCall(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleStringFunctionCall(JNIEnv* java_env, jobject);
#ifdef __cplusplus
}
#endif
#endif
#include <jni.h>
class JniTestClass {
typedef jint(JNICALL * GetCreatedJavaVMs)(JavaVM**, jsize, jsize*);
public:
void setup();
void callJavaStringFunction();
void callJavaIntFunction();
void throwException(jthrowable ex);
private:
jobject myObject;
jclass myClass;
JNIEnv* env;
};
#include "JniTestCpp.h"
#include "JniTestClass.h"
extern "C"
{
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_setup(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleIntFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaIntFunction();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleIntFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaIntFunction();
jniTestClass.callJavaIntFunction();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleStringFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaStringFunction();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleStringFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaStringFunction();
jniTestClass.callJavaStringFunction();
}
}
下面我展示了hs_err_pidXXX.log文件中的10个顶层堆栈帧:
让我惊讶的是,如果我在JniTestCpp.cpp中没有声明JniTestClass JniTestClass
为静态对象,而是声明它并在每个方法中调用setup()
,如下图所示,它不会崩溃,但会产生预期的结果。另外,我必须说,调用doubleIntFunctionCall()时工作是相当奇怪的代码>但不是doubleStringFunctionCall()代码>
JniTestCpp.cpp-这不会崩溃
#include <jni.h>
#ifndef _Included_jnitest_JniTestJava
#define _Included_jnitest_JniTestJava
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_setup(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleIntFunctionCall(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleIntFunctionCall(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleStringFunctionCall(JNIEnv* java_env, jobject);
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleStringFunctionCall(JNIEnv* java_env, jobject);
#ifdef __cplusplus
}
#endif
#endif
#include <jni.h>
class JniTestClass {
typedef jint(JNICALL * GetCreatedJavaVMs)(JavaVM**, jsize, jsize*);
public:
void setup();
void callJavaStringFunction();
void callJavaIntFunction();
void throwException(jthrowable ex);
private:
jobject myObject;
jclass myClass;
JNIEnv* env;
};
#include "JniTestCpp.h"
#include "JniTestClass.h"
extern "C"
{
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_setup(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleIntFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaIntFunction();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleIntFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaIntFunction();
jniTestClass.callJavaIntFunction();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_singleStringFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaStringFunction();
}
JNIEXPORT void JNICALL Java_jnitest_DllFunctions_doubleStringFunctionCall(JNIEnv* java_env, jobject) {
JniTestClass jniTestClass;
jniTestClass.setup();
jniTestClass.callJavaStringFunction();
jniTestClass.callJavaStringFunction();
}
}
很抱歉发了这么长的帖子,但这是我唯一能明确表达我的问题的方式
更新
在函数void JniTestClass::callJavaStringFunction()
中,如果我将其更改为以下内容:
void JniTestClass::callJavaStringFunction() {
jmethodID myMethod = env->GetMethodID(myClass, "javaStringFunction", "(Ljava/lang/String;)V");
if (env->ExceptionCheck()) {
throwException(env->ExceptionOccurred());
}
jstring j_string = env->NewStringUTF("String!");
env->CallVoidMethod(myObject, myMethod, j_string);
if (env->ExceptionCheck()) {
throwException(env->ExceptionOccurred());
}
env->DeleteLocalRef(j_string);
}
现在我在使用NewStringUTF()
创建的jstring
上调用DeleteLocalRef()
,程序仍会崩溃,但会打印出此异常消息:
java.lang.NoSuchMethodError: javaStringFunction
您的代码中有几个错误
jobject myObject
和jclass myClass
在JNI调用中重用
默认情况下,在JNI方法中创建的所有jobject
都是本地引用。每当JNI方法返回时,所有本地引用都会自动释放
如果要跨方法调用重用jobject
(或jclass
,它也是一个对象引用),则应使用将其转换为全局引用。当不再需要全局引用时,它应该被删除,否则引用的对象将永远不会被垃圾收集
JNIEnv*
被缓存
一般来说,JNIEnv*
永远不应该存储起来以供以后重用。相反,您应该使用提供的JNIEnv*
作为每个JNI函数的第一个参数。或者也可以通过电话获得。请注意,每个线程都有自己的JNIEnv*
,这不适用于其他线程
H.Guijt:它怎么不是C++ +?H.Guijt,我只是展示了相关的代码的一部分。@ MartinRindar:你应该发布一个完整的最小的例子来复制这个问题。首先,你不检查任何返回值。您只需假设GetMethodID
有效。其次,你的代码正在崩溃,你不知道如何修复它,那么你怎么知道什么是相关的呢?发帖:“我曾希望我所展示的足以让别人告诉我为什么GetMethodID()在NewStringUTF()出现时崩溃。”一般来说,它不会。如果我将您的代码插入测试项目(用其他内容替换myClass
和myFunction
),它也不会崩溃。因此,您发布的代码不足以重现问题。感谢您为我清除这些内容!周一,我将根据您的评论进行更改,并报告结果。使用NewGlobalRef将jobject myObject
和jclass myClass
转换为全局引用成功。谢谢