多次调用本机方法时发生致命错误

问题描述 投票:0回答:1

我最近开始使用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 ++数学库看起来几乎与此库完全相同(至少数学是相同的,但是不会产生相同的错误。

[如果有人可以在这里帮助我,我将非常感激,谢谢!

java c++ java-native-interface
1个回答
0
投票

可能会有更多问题,但是我注意到的第一个问题是,您无法像您那样缓存FindClass的结果。您需要使用NewGlobalRef

© www.soinside.com 2019 - 2024. All rights reserved.