当前位置: 首页 > 面试题库 >

JNI-在Java和本机代码之间传递大量数据

伏砚
2023-03-14
问题内容

我正在努力实现以下目标:

1)我在Java端有一个字节数组,代表一个图像。

2)我需要授予我的本机代码访问权限。

3)本机代码使用GraphicsMagick解码此图像,并通过调用resize创建一堆缩略图。它还计算图像的感知哈希,它可以是矢量或unint8_t数组。

4)一旦我将这些数据返回到Java端,不同的线程将读取它。缩略图将通过HTTP上传到某些外部存储服务。

我的问题是:

1)将字节从Java传递到本机代码的最有效方法是什么?我可以将其作为字节数组访问。我没有看到将其作为字节缓冲区(包装此字节数组)与此处的字节数组传递任何特殊优势。

2)将这些缩略图和感知哈希返回给Java代码的最佳方法是什么?我想到了一些选择:

(i)我可以在Java中分配一个字节缓冲区,然后将其传递给本机方法。然后,本机方法可以对其进行写入并在完成后设置限制,并返回写入的字节数或表示成功的布尔值。然后,我可以对字节缓冲区进行切片和切块,以提取不同的缩略图和可感知的哈希,并将其传递给将上传缩略图的不同线程。这种方法的问题是我不知道要分配多少大小。所需的大小取决于生成的缩略图的大小(我事先不知道)和缩略图的数量(我确实知道)。

(ii)一旦知道所需的大小,我也可以用本机代码分配字节缓冲区。我可以根据我的自定义打包协议将blob转移到正确的区域,然后返回此字节缓冲区。(i)和(ii)都看起来很复杂,因为自定义打包协议必须指示每个缩略图的长度和感知哈希。

(iii)定义一个Java类,该类具有以下字段:缩略图:字节缓冲区数组和感知哈希:字节数组。当我知道所需的确切大小时,可以在本机代码中分配字节缓冲区。然后,我可以将GraphicsMagick
Blob中的字节存储到每个字节缓冲区的直接地址。我假设还有一些方法可以设置写入字节缓冲区的字节数,以便Java代码知道字节缓冲区的大小。设置字节缓冲区后,我可以填写Java对象并返回它。与(i)和(ii)相比,我在这里创建了更多的字节缓冲区以及Java对象,但是避免了自定义协议的复杂性。(i),(ii)和(iii)的基本原理-
鉴于我对这些缩略图所做的唯一一件事就是上传它们,

(iv)定义一个Java类,该类具有用于缩略图的字节数组(而不是字节缓冲区)和用于感知哈希的字节数组的数组。我用本机代码创建了这些Java数组,并使用SetByteArrayRegion从GraphicsMagick
blob复制了字节。与以前的方法相比,缺点在于,当将此字节数组从堆复制到某个直接缓冲区上载时,现在在Java领域中还会有另一个副本。不知道我是否会在这里相对于(iii)节省任何事情。

任何建议都很棒。

编辑:@main建议一个有趣的解决方案。我正在编辑我的问题,以跟进该选项。如果我想像@main一样将本机内存包装在DirectBuffer中,我怎么知道何时可以安全地释放本机内存?


问题答案:

将字节从Java传递到本机代码的最有效方法是什么?我可以将其作为字节数组访问。我没有看到将其作为字节缓冲区(包装此字节数组)与此处的字节数组传递任何特殊优势。

Direct的最大优点ByteBuffer是您可以GetDirectByteBufferAddress在本机端进行调用,并且立即有了指向缓冲区内容的指针,而没有任何开销。如果传递字节数组,则必须使用GetByteArrayElementsReleaseByteArrayElements(它们可能会复制该数组)或关键版本(它们会使GC暂停)。因此,使用直接ByteBuffer指令可以对代码的性能产生积极影响。

如您所说,(i)不起作用,因为您不知道该方法将返回多少数据。(ii)由于该定制包装协议而过于复杂。我将使用(iii)的修改版本:不需要该对象,您可以返回ByteBuffers
的数组,其中第一个元素是哈希,其他元素是缩略图。这样您就可以 丢掉所有memcpys了!这就是直接的全部要点ByteBuffer:避免复制。

码:

void Java_MyClass_createThumbnails(JNIEnv* env, jobject, jobject input, jobjectArray output)
{
    jsize nThumbnails = env->GetArrayLength(output) - 1;
    void* inputPtr = env->GetDirectBufferAddress(input);
    jlong inputLength = env->GetDirectBufferCapacity(input);

    // ...

    void* hash = ...; // a pointer to the hash data
    int hashDataLength = ...;
    void** thumbnails = ...; // an array of pointers, each one points to thumbnail data
    int* thumbnailDataLengths = ...; // an array of ints, each one is the length of the thumbnail data with the same index

    jobject hashBuffer = env->NewDirectByteBuffer(hash, hashDataLength);
    env->SetObjectArrayElement(output, 0, hashBuffer);

    for (int i = 0; i < nThumbnails; i++)
        env->SetObjectArrayElement(output, i + 1, env->NewDirectByteBuffer(thumbnails[i], thumbnailDataLengths[i]));
}

编辑:

我只有一个字节数组可用于输入。不会将字节数组包装在字节缓冲区中仍然产生相同的负担吗?我也对数组使用了以下语法:http : //developer.android.com/training/articles/perf-
jni.html#region_calls。虽然仍然可以复制。

GetByteArrayRegion总是写到缓冲区,因此每次都创建一个副本,所以我建议GetByteArrayElements使用。将数组复制到直接ByteBufferJava端上也不是最好的主意,因为您仍然拥有该副本,如果GetByteArrayElements将数组固定,最终可以避免。

如果我创建包装本地数据的字节缓冲区,谁负责清理它?我之所以做memcpy,只是因为我认为Java不知道何时释放它。该内存可能在堆栈上,在堆上或在某些自定义分配器上,这似乎会导致错误。

如果数据在堆栈上,则 _ 必须_
将其复制到Java数组中,该数组ByteBuffer是用Java代码创建的直接指令或在堆上的某个ByteBuffer位置(并且该指令指向该位置)。如果它在堆上,那么只要可以确保没有人释放内存,就可以安全地使用ByteBuffer您使用创建的直接NewDirectByteBuffer指令。释放堆内存后,您必须不再使用该ByteBuffer对象。ByteBuffer使用NewDirectByteBufferGC
创建的直接目录时,Java不会尝试删除本机内存。您必须手动进行处理,因为您还手动创建了缓冲区。



 类似资料:
  • 问题内容: 我一直遵循本文中@tulskiy的建议,通过JNI将Java中的c指针存储在Java中通过JNI在C和Java之间传递指针 诀窍是将指针转换为jlong​​。所以从c我有 我返回一个jlong​​(始终为64位),因为我希望我的代码在64位和32位系统上都能工作。64位计算机上64位指针在内存中的大小为64,而在32位计算机上,内存中指针的大小为32位。 问题是在32位计算机上,我收到

  • 问题内容: 抱歉,如果我的问题是菜鸟。我正在使用pgsql 8.4运行Django 1.2,并且需要在从用户处获得输入后运行Java程序,进行一些计算并将结果返回给用户。 我是否可以知道在Java和Python Django之间传递数据的最佳方法是什么,以便解决上述情况?我听说过Jython,但是根据Django文档,它需要扩展名jython- Django,但目前不支持django1.2.x。我

  • 问题内容: 我有一个C结构。 我想将数据从Java映射到C结构,如何使用JNI进行处理?我不会编程Java代码。Java程序员只是想知道他应该以哪种形式向我发送数据?他是否应该期待更多细节 我目前正在通过用包含2列的CSV文件填充结构实例来测试我的代码。 我也想从我的C代码向Java应用程序返回3个double值。 问题答案: 向您的Java程序员提供以下声明: 在您的C代码中,您将能够使用传递的

  • 我是Java的新手,正在尝试制作一个基本的体质量计算器。我的问题是我需要问问题,转换度量值,然后将它传递给另一个方法,然后在一个单独的方法中显示结果。我在下面添加了代码,但每次都返回一个1.0作为答案。 更新的代码,现在正在获取;输入体重(磅):180输入身高(英寸):68计算出的体重指数为:1.1415618118905313 E-5建立成功(总时间:3秒)

  • 问题内容: 我敢肯定这很简单,但是我一直无法使其正常工作。我需要让我的主要python脚本调用另一个python脚本,并将变量从原始脚本传递到我已调用的脚本 举一个简单的例子,我的第一个脚本是 我的第二个脚本是 我希望它可以打印x但我得到 我不确定导入是否是实现此目标的正确方法,但是如果有人可以通过简单的方式对此有所了解,那就太好了! 谢谢, 编辑 阅读评论后,我想我会扩展我的问题。Aswin M

  • 问题内容: 我花了过去的几个小时在这里和其他地方进行阅读和实验,但是我并没有真正理解我确信这是一个非常基本的概念:在不同的函数之间传递值(作为变量)。 例如,我将一堆值分配给一个函数中的列表,然后想稍后在另一个函数中使用该列表: 基于对函数参数的作用的理解,我希望这样做如下: 将“列表”初始化为空列表;打电话给main(至少,我知道我没事…) 在defineAList()中,将某些值分配给列表;然