簡體   English   中英

多次調用本機方法時出現致命錯誤

[英]Fatal error when calling native method more than once

我最近開始使用 C++ 中的內在函數構建自己的數學庫。 我終於完成了通過 JNI 接口創建 Java 綁定並解決了所有錯誤。

奇怪的是,當我在 java 代碼中多次調用 Vec3f 的 add(Vec3f) 方法時,會發生致命異常。 由於日志 output 相當不直觀,我認為更有經驗的開發人員可能比我更了解這一點。

我將只分享一個 class,因為它們都基於相同的原理。

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 中閱讀有關 function arguments 的信息時,我遇到了一些關於返回對象生命周期的警告,老實說,我並不真正理解。

我的純 c++ 數學庫看起來與這個幾乎相同(至少數學是相同的),但它不會產生相同的錯誤。

如果有人可以在這里幫助我,我將不勝感激,謝謝!

可能還有更多問題,但我注意到的第一個問題是您無法以您的方式緩存FindClass的結果。 如果要在 JNI 方法調用中保留 class ID,則需要使用NewGlobalRef

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM