通过JNI将数据从C传递到Java的快速方法

时间:2015-09-03 13:45:42

标签: java c java-native-interface

我在c中使用了一些快速通信库,但我的应用程序的其余部分是用Java编写的。所以我想将收到的数据传递给我的java应用程序。

作为测试,我每隔5ms收到1000次消息。 这导致发件人应用程序完成发送时。接收器应用程序仍需要时间来处理数据。

如果我删除该行

(*g_env)->CallVoidMethod(g_env, store_callback, methodHandleMessage, top,
    ts, fo, msg);

接收方应用程序在发送方发送完最后一条消息后立即完成。

有没有机会加快速度?

void onMessageReceived(char* topic, char* timestamp, char* format,
    char* message) {
JNIEnv * g_env;
int getEnvStat = (*g_vm)->GetEnv(g_vm, (void **) &g_env,
JNI_VERSION_1_8);
if (getEnvStat == JNI_EDETACHED) {
    if ((*g_vm)->AttachCurrentThread(g_vm, (void **) &g_env, NULL) != 0) {
        puts("Failed to attach");
        fflush(stdout);
    }
}

if (methodHandleMessage) {
} else {
    jclass clazz = (*g_env)->GetObjectClass(g_env, store_callback);
    methodHandleMessage =
            (*g_env)->GetMethodID(g_env, clazz, "handleMessage",
                    "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");

}
jstring top = (*g_env)->NewStringUTF(g_env, topic);
jstring ts = (*g_env)->NewStringUTF(g_env, timestamp);
jstring fo = (*g_env)->NewStringUTF(g_env, format);
jstring msg = (*g_env)->NewStringUTF(g_env, message);

 //This line takes too long!
(*g_env)->CallVoidMethod(g_env, store_callback, methodHandleMessage, top,
        ts, fo, msg);

}

2 个答案:

答案 0 :(得分:0)

您可以通过将类标记为handleMessage来节省final调用的一些时间,这将消除执行虚拟查找的需要。

如果这还不够,并且如果不需要异步调用handleMessage,则可以避免直接调用它,而是将参数存储在您将创建和管理的本地C数组中。然后,您可以通过MonitorEnter锁定它,调用GetMethodID(g_env, clazz, "notify", "()V"),然后使用MonitorExit解锁来通知Java对象有可用的数据。

然后在Java方面,store_callback实例将有一个等待通知的专用线程,如下所示:

while (true) {
    synchronized (self) {
        wait();
        // we were informed that we received new data: now pull it out and process
        String[][] args = getBufferedArgumentsViaJNI();
        for (String[] arg : args) {
            handleMessage(arg[0], arg[1], arg[2], arg[3]);
        }
    }
}

回到原生的一面,你需要实现getBufferedArgumentsViaJNI来返回并清除所有缓冲参数的JNI数组(作为练习留给读者)。

作为一个精明的读者,您可能想知道为什么这会更快,因为您仍然需要在onMessageReceived中调用JNI方法以及执行JNI同步,因此它仍然会产生JNI开销。答案是,虽然我无法保证消息处理总体上更快,但“等待”调用可能会明显加快,因为wait()在我见过的每个JVM上作为本机方法实现

答案 1 :(得分:0)

java方法可能为空,但您每次仍调用GetEnv(),并创建4个可复制到Java的字符串。这些字符串有多大?我试着用一些10-20个字符的字符串调用这个方法并测量它。另一个简单的解决方案(如果方法调用非常慢)用数组而不是字符串调用它,甚至可能将几个字符串打包到一个数组中。或者您也可以使用共享缓冲区(http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html#GetDirectBufferAddress