【发布时间】:2014-02-04 16:21:15
【问题描述】:
我正在通过 JNI 调用一个 C 函数,该函数将返回一个字节数组。这就像一个魅力,直到我试图通过删除我打印到控制台的不必要的调试输出来清理访问 Java 类。这些输出之一是在 JNI 调用和访问它返回的数组之间生成的:
long ctime = System.currentTimeMillis();
byte[] arrayFromC = _wrapperObject.someMethod(someByteArray, k, n);
System.out.println(System.currentTimeMillis()-ctime);
byte[] newByteArray = doSomething(arrayFromC, n);
当我删除 println 调用时,我突然开始出现分段错误:
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007fe1d410f751, pid=6875, tid=140607705560832
#
# JRE version: OpenJDK Runtime Environment (7.0_51) (build 1.7.0_51-b00)
# Java VM: OpenJDK 64-Bit Server VM (24.45-b08 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# V [libjvm.so+0x5c8d51] jni_GetByteArrayElements+0x61
当 JRE 尝试执行上面显示的最后一行代码时,会触发分段错误。如果我尝试将其替换为访问数组的任何其他操作(例如仅将第一个数组元素分配给变量),这也会触发分段错误。
这就是我在 C 函数中返回数组的方式:
// c[] is a char array of length mlen which at this point has already been filled by a library call
jbyteArray out = (*env)->NewByteArray(env, mlen);
jbyte *jBuf = (jbyte *)calloc(sizeof(jbyte), mlen);
for (int i = 0; i < mlen; i ++) {
jBuf[i] = (jbyte)c[i];
}
(*env)->SetByteArrayRegion(env, out, 0, mlen, jBuf);
free(jBuf);
return out;
这就是包装类中的方法所做的(基本上删除了我正在使用的 C 库添加到数组中的一些零填充,然后返回它):
byte[] cpadded = jni_c_function(mpadded, mpadded.length, n, k);
return Arrays.copyOfRange(cpadded, CZEROBYTES, cpadded.length);
我可以通过在进行 JNI 调用和访问数组之间打印我的调试输出或仅调用 this.wait(10) 来避免分段错误,因此我怀疑 JNI 调用实际上可能在它实际完成之前返回填充数组的内存区域。不过,我似乎找不到任何关于这种行为的文档和/或类似问题,如果是这样的话,我会期望的。
关于如何在不打印不必要的内容或只是等待任意时间的情况下解决此问题的任何想法?
【问题讨论】:
-
您可以尝试使用同步块。请参阅this文档中有关同步的部分。
-
@gmorrow 我只是尝试将 C 操作以及主类中的方法调用都包含在同步块中。不幸的是,这并不能解决问题。为了更清楚一点:我没有在任何地方明确调用新线程。我希望这些操作是按顺序处理的,所以无论如何都不需要使用同步。不知何故,发生的事情似乎比我预期的要晚得多(而且不按顺序)。
-
我不确定为什么这可以解决段错误,但是您的 C 代码执行了太多冗余步骤。试试这个:
jbyteArray out = (*env)->NewByteArray(env, mlen); (*env)->SetByteArrayRegion(env, out, 0, mlen, (jbyte*)c); free(jBuf); return out; -
@AlexCohn 感谢您指出这一点。这似乎解决了问题。
标签: java c arrays java-native-interface segmentation-fault