我最近开始使用C ++中的内在函数构建自己的数学库。我终于通过JNI接口创建了Java绑定,并消除了所有错误。
奇怪的是,当我在Java代码中多次调用Vec3f的add(Vec3f)方法时,会发生致命异常。由于日志输出相当不直观,因此我认为经验丰富的开发人员可能比我更了解这一点。
我将只分享一堂课,因为他们都是基于相同的原理。
Vec3f c ++:
#include"com_math_Vec3f.h" // includes jni.h
#include<immintrin.h>
static bool initialized = false;
static jclass this_class;
static jfieldID values_id;
static jmethodID init_id;
static void initialize(JNIEnv *env)
{
if (!initialized)
{
initialized = true;
this_class = env->FindClass("Lcom/math/Vec3f;");
values_id = env->GetFieldID(this_class, "values", "[F");
init_id = env->GetMethodID(this_class, "<init>", "(FFF)V");
}
}
/*
* Class: com_math_Vec3f
* Method: add
* Signature: (Lcom/math/Vec3f;)Lcom/math/Vec3f;
*/
JNIEXPORT jobject JNICALL Java_com_math_Vec3f_add
(JNIEnv *env, jobject this_object, jobject v)
{
initialize(env);
jfloatArray v1 = reinterpret_cast<jfloatArray>(env->GetObjectField(this_object, values_id));
jfloatArray v2 = reinterpret_cast<jfloatArray>(env->GetObjectField(v, values_id));
jfloat *v1_a = env->GetFloatArrayElements(v1, nullptr);
jfloat *v2_a = env->GetFloatArrayElements(v2, nullptr);
__m128 m1 = _mm_set_ps(v1_a[2], v1_a[1], v1_a[0], 0.0f);
__m128 m2 = _mm_set_ps(v2_a[2], v2_a[1], v2_a[0], 0.0f);
__m128 sum = _mm_add_ps(m1, m2);
jobject res = env->NewObject(this_class, init_id, sum[1], sum[2], sum[3]);
return res;
}
Vec3f Java:
import com.math.libloader.LibLoader;
public class Vec3f {
static {
LibLoader.init();
}
private float[] values;
public Vec3f(float x, float y, float z) {
values = new float[]{ x, y, z };
}
public Vec3f() {
this(0.0f, 0.0f, 0.0f);
}
public Vec3f(Vec3f v) {
this(v.values[0], v.values[1], v.values[2]);
}
public native Vec3f add(Vec3f v);
public float getX() {
return values[0];
}
public float getY() {
return values[1];
}
public float getZ() {
return values[2];
}
}
我从这些类中切出了很多不必要的代码
LibLoader java(我的dll被放在类路径中的jar中:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class LibLoader {
private static final int BUFF_SIZE = 10000;
static {
InputStream stream = ClassLoader.getSystemResourceAsStream("math.dll");
if (stream == null) {
System.err.println("Error loading native libs for math.jar");
System.exit(-1);
}
File tempDir = new File("math-temp");
tempDir.mkdirs();
tempDir.deleteOnExit();
File tempFile = new File(tempDir, "math.dll");
try {
tempFile.createNewFile();
tempFile.deleteOnExit();
} catch (IOException e) {
System.err.println("Error loading native libs for math.jar. Cannot create temp file");
System.exit(-1);
}
try (FileOutputStream outStream = new FileOutputStream(tempFile)) {
while (stream.available() > 0) {
outStream.write(stream.readNBytes(BUFF_SIZE));
}
stream.close();
outStream.close();
} catch (IOException e) {
System.err.println("Error loading native libs for math.jar. Internal error");
System.exit(-1);
}
System.load(tempFile.getAbsolutePath());
}
public static void init() {}
private LibLoader() {}
}
最后,我的测试代码(Main Java):
import com.math.Vec3f;
public class Main {
public static void main(String[] args) {
Vec3f v1 = new Vec3f(-4.3f, 0.7f, 6.1f);
Vec3f v2 = new Vec3f(3.5f, -6.7f, 3.1f);
Vec3f t1 = v1.add(v2);
Vec3f t2 = v1.add(v2); // fatal exception occurs here
}
}
此外,如果对任何人有用,则摘录为日志:
--------------- S U M M A R Y ------------
Command Line: -Djava.library.path=C:\Dev\java\Projects\Math\dist\jni; -Dfile.encoding=Cp1252 --module-path=C:\Dev\java\Projects\Math Test\bin;C:\Dev\java\Projects\Math Test\libs\math.jar -Djdk.module.main=com.test com.test/main.Main
Host: Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz, 4 cores, 7G, Windows 10 , 64 bit Build 18362 (10.0.18362.778)
Time: Wed May 20 00:17:23 2020 W. Europe Daylight Time elapsed time: 0 seconds (0d 0h 0m 0s)
--------------- T H R E A D ---------------
Current thread (0x000001dd8c4cd000): JavaThread "main" [_thread_in_vm, id=20616, stack(0x0000002906300000,0x0000002906400000)]
Stack: [0x0000002906300000,0x0000002906400000], sp=0x00000029063ff2c0, free space=1020k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [jvm.dll+0x3ce5f2]
V [jvm.dll+0x3cc3e4]
C [math.dll+0x7596]
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j com.math.Vec3f.add(Lcom/math/Vec3f;)Lcom/math/Vec3f;+0 com.math
j main.Main.main([Ljava/lang/String;)V+41 com.test
v ~StubRoutines::call_stub
siginfo: EXCEPTION_ACCESS_VIOLATION (0xc0000005), reading address 0x0000000000000005
[在阅读有关JNI的函数参数时,我遇到了一些关于返回对象的生存期的警告,老实说我并不太了解。
我的纯c ++数学库看起来几乎与此库完全相同(至少数学是相同的,但是不会产生相同的错误。
[如果有人可以在这里帮助我,我将非常感激,谢谢!
可能会有更多问题,但是我注意到的第一个问题是,您无法像您那样缓存FindClass
的结果。您需要使用NewGlobalRef
。