首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JNIEnv::NewObject()抛出java.lang.InstantiantionException

JNIEnv::NewObject()抛出java.lang.InstantiantionException
EN

Stack Overflow用户
提问于 2022-01-18 23:45:48
回答 1查看 103关注 0票数 0

当C函数返回一个非零错误代码时,我试图在一些JNI代码中调用JNIEnv::NewObject()

事件的顺序如下:

  1. 调用C函数。
  2. 如果返回代码为非零,则调用抛出自定义excpetion的助手函数.

为了使我能够抛出它,我试图构造的类是:

代码语言:javascript
复制
public final class HseException extends Exception {
    private static final long serialVersionUID = 8995408998818557762L;

    private final int errno;
    private final Context ctx;

    /* Only called from C */
    HseException(final String message, final int errno, final Context ctx) {
        super(message);

        this.errno = errno;
        this.ctx = ctx;
    }

    public Context getContext() {
        return this.ctx;
    }

    public int getErrno() {
        return this.errno;
    }

    public static enum Context {
        NONE
    }
}

在我的代码中,我在全局结构中缓存类和构造函数的jclassjmethodID,但是代码看起来如下:

代码语言:javascript
复制
    globals.com.micron.hse.HseException.class =
        (*env)->FindClass(env, "com/micron/hse/HseException");
    globals.com.micron.hse.HseException.init = (*env)->GetMethodID(
        env,
        globals.com.micron.hse.HseException.class,
        "<init>",
        "(Ljava/lang/String;ILcom/micron/hse/HseException$Context;)V");

    globals.com.micron.hse.HseException.Context.class =
        (*env)->FindClass(env, "com/micron/hse/HseException$Context");
    globals.com.micron.hse.HseException.Context.NONE = (*env)->GetStaticFieldID(
        env,
        globals.com.micron.hse.HseException.Context.class,
        "NONE",
        "Lcom/micron/hse/HseException$Context;");

注意,上面的代码位于我库的JNI_OnLoad()函数中。这个函数完成时没有出错,所以这告诉我至少我的类和方法被正确加载了。

最后,这里是我的助手函数,在这里我抛出了我的自定义异常类型:

代码语言:javascript
复制
/* hse_err_t is a scalar type.
 * hse_strerror() creates a string out of that scalar.
 * hse_err_to_ctx() gets the enum context value embedded within the scalar.
 * hse_err_to_errno() gets the errno value embedded within the scalar.
 */
jint
throw_new_hse_exception(JNIEnv *env, hse_err_t err)
{
    assert(env);
    assert(err);

    const size_t needed_sz = hse_strerror(err, NULL, 0);
    char        *buf = malloc(needed_sz + 1);
    if (!buf)
        return (*env)->ThrowNew(
            env,
            globals.java.lang.OutOfMemoryError.class,
            "Failed to allocate memory for error buffer");

    hse_strerror(err, buf, needed_sz + 1);

    const jstring message = (*env)->NewStringUTF(env, buf);
    free(buf);
    if ((*env)->ExceptionCheck(env))
        return JNI_ERR;

    const int              rc = hse_err_to_errno(err);
    const enum hse_err_ctx ctx = hse_err_to_ctx(err);

    jfieldID err_ctx_field = NULL;
    switch (ctx) {
        case HSE_ERR_CTX_NONE:
            err_ctx_field = globals.com.micron.hse.HseException.Context.NONE;
            break;
    }

    assert(err_ctx_field);

    const jobject err_ctx_obj = (*env)->GetStaticObjectField(
        env, globals.com.micron.hse.HseException.Context.class, err_ctx_field);
    if ((*env)->ExceptionCheck(env))
        return JNI_ERR;

    const jobject hse_exception_obj = (*env)->NewObject(
        env,
        globals.com.micron.hse.HseException.class,
        globals.com.micron.hse.HseException.init,
        message,
        rc,
        err_ctx_obj);
    if ((*env)->ExceptionCheck(env))
        return JNI_ERR;

    return (*env)->Throw(env, (jthrowable)hse_exception_obj);
}

我知道的一个事实是,(*env)->NewObject()调用是引发异常的原因,因为前后的异常检查都会告诉我这一点。(*env)->NewStringUTF()调用是成功的,并且包含它应该包含的字符串。还成功地检索了上下文字段。

我不理解的是为什么我要获得InstantiationExceptionThrows部分的JNIEnv::NewObject()标记为:

代码语言:javascript
复制
THROWS:
InstantiationException: if the class is an interface or an abstract class.

OutOfMemoryError: if the system runs out of memory.

Any exceptions thrown by the constructor.

我的类不是一个接口,也不是一个抽象类,那么从哪里可以生成这个异常呢?奇怪的是,我发誓这以前起过作用,但是由于我是从零开始编写这些Java绑定的,所以我一直在覆盖提交并强制推进到我的分支。

任何帮助都是非常感谢的。不幸的是,异常上的getMessage()返回null,这一点都没有帮助。JVM也没有告诉我我做错了什么。

一个可能有用的细节是,当我尝试调用JNIEnv::ThrowNew()时(在将(Ljava/lang/String;)V构造函数放入同一个HseException类之后),jni_ThrowNew()分段错误,我不明白为什么。当我保存jclass时,类是有效的,而且我知道,由于我已经检查了指针,所以它所存储的内存不会以任何方式被覆盖。

所有这些代码所在的回购程序是:https://github.com/hse-project/hse-java。未完成的产品,但至少它是可构建和测试可以运行。如果有人决定克隆并构建回购程序,我将在这里重复以下说明:

代码语言:javascript
复制
meson build
ninja -C build
meson test -C build -t 0 KvsTest # I am using this test to exercise the code path

我明天的目标是尝试以较小的方式重现这个问题。我还可以尝试查看OpenJDK代码,假设这是JNI接口所在的位置。如果我仔细看的话,我可能会找到生成异常的代码行。

编辑:我做了一个测试,在当前代码中添加了一个主函数和一个本机函数,其唯一目的是从C中抛出一个异常。代码看起来类似于:

代码语言:javascript
复制
private static native void throwException();

public static void main(String[] args) {
    System.load("/path/to/.so");
    throwException();
}

本机功能的实现是:

代码语言:javascript
复制
void Java_com_micron_hse_Hse_throwException
  (JNIEnv *env, jclass hse_cls)
{
    (void)hse_cls;

    /* Generate error */
    hse_err_t err = hse_kvdb_txn_begin(NULL, NULL);

    throw_new_hse_exception(env, err);
}

这将在执行java -jar path/to/jar后打印以下内容

代码语言:javascript
复制
Exception in thread "main" com.micron.hse.HseException: lib/binding/kvdb_interface.c:1046: Invalid argument (22)
        at com.micron.hse.Hse.throwException(Native Method)
        at com.micron.hse.Hse.main(Hse.java:28)

这正是我所期望的印刷,所以现在我要说我比我开始的时候更迷失了。由于某些原因,在我的测试上下文中,会引发InstantiationException。不确定使用JAR的应用程序是否会遇到相同的问题,或者它是否只是测试上下文问题。

编辑2:

将主方法从前面的编辑更改为以下内容,这正是我的测试所做的:

代码语言:javascript
复制
    public static void main(String[] args) throws HseException {
        try {
            loadLibrary(Paths.get("/home/tpartin/Projects/hse-java/build/src/main/c/libhsejni-2.so"));

            init();

            final Kvdb kvdb = Kvdb.open(Paths.get("/media/hse-tests"));
            final Kvs kvs = kvdb.kvsOpen("kvs");

            kvs.delete((byte[])null);

            kvs.close();
            kvdb.close();
        } finally {
            // fini();
        }
    }

并能够适当地抛出C中的异常。这一定意味着我的测试环境出了问题。

编辑3:另一条线索。在一次测试中,此问题将生成InstantiationException。在另一个测试中,此问题是jni_NewObject中的分段错误。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-01-19 23:23:48

我的问题是,我坚持jclass等人。引用时间太长了。

先前的问题:为什么我不应该在JNI中重用jclass和/或jmethodID?

Java文档:参考文献

JNI函数返回的所有Java对象都是本地引用。

感谢Andrew在问题的评论中指出了这一点。我强调了他在这个答复中的意见,并将把它作为答案。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70763746

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档