【问题标题】:JNI convert a String to char *, returned by an instance methodJNI 将 String 转换为 char *,由实例方法返回
【发布时间】:2016-05-27 21:59:01
【问题描述】:

在我的程序中,C 是从 Java 调用的,C 函数可以在 Java 上下文之外调用,但有时需要一些 Java 资源。我应该解释一下程序是什么......所以C库是一个可以加载C插件的插件系统,插件有时需要访问Java资源,例如检索String id。

因此,当插件系统被加载时,JNI 会调用一个 init 函数,因此可以存储 JNIEnv *jobject 以供插件进一步访问。

在 java 类中我提供了访问这些资源的实例方法,例如我有方法

private String getId() {
  return "Bryan";
}

C插件系统有一个功能

char *get_id(char *id) {
  jobject jobj = (*jvm.env)->CallObjectMethod(jvm.env, jvm.this, jvm.getId);
  jstring jid = jobj;
  if (jid == NULL) 
    debug("get_id", RED "jid NULL");
   else 
     debug("get_id", RED "jid not null"); */
  debug("get_id", RED "in get_id, method called");
  const char *cid = (*jvm.env)->GetStringUTFChars(jvm.env, jid, NULL);
  debug("get_id", RED "converted to c string: %s", cid);
  strcpy(id, cid);
  debug("get_id", RED "string copied");
  (*jvm.env)->ReleaseStringUTFChars(jvm.env, jid, cid);
  debug("get_id", RED "string released");
  return id;
}

其中jvm是一个结构体,其中包含envobj字段,对应于初始化时存储的JNIEnv *jobjectjvm.getIdgetIdJava实例方法的methodID同时初始化。 调试宏只是调用printfflush,它可以帮助我调试程序。

这是调用get_id后的输出:

DEBUG IN plugin_system.c LINE 339:
In get_id
in get_id, calling method...
DEBUG IN plugin_system.c LINE 343:
In get_id
jid NULL
DEBUG IN plugin_system.c LINE 346:
In get_id
in get_id, method called
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007fb6bcb6cd70, pid=25254, tid=0x00007fb6974be700
#
# JRE version: OpenJDK Runtime Environment (8.0_92-b14) (build 1.8.0_92-b14)
# Java VM: OpenJDK 64-Bit Server VM (25.92-b14 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# V  [libjvm.so+0x675d70]
#
# Core dump written. Default location: /home/kowa/code/reseaux/projet/ringo/java/bin/core or core.25254
#
# An error report file with more information is saved as:
# /home/kowa/code/reseaux/projet/ringo/java/bin/hs_err_pid25254.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
[4]    25254 abort (core dumped)  java Jring Nick 9999 8888 1

如您所见,对 Java getId 的调用(看起来)是成功的,但核心转储是由 GetStringUTFChars 触发的。

怎么了?

【问题讨论】:

  • 您的jvm 对象是什么?结构?一类?你怎么填写?通常,您不能缓存诸如 JVM env 值之类的值。除非您创建 global reference,否则您也不能缓存对象或方法 ID。
  • 这是一个struct jvm { JNIEnv * env; jobject this; jclass thisClass; methodID getId; };,该字段是在加载库后,在java调用本机init函数后填充的。
  • 首先,您对 C 字符串使用的字符集和编码是系统默认的还是特定的?

标签: java c java-native-interface


【解决方案1】:

您不能缓存 JVM 的 env。见Keeping a global reference to the JNIEnv environment,尤其是this answer

JNI接口指针(JNIEnv)只在当前有效 线。如果另一个线程需要访问 Java VM,它必须 首先调用 AttachCurrentThread() 将自身附加到 VM 并获取 一个 JNI 接口指针。连接到 VM 后,本机线程 就像在本机内部运行的普通 Java 线程一样工作 方法。本机线程一直连接到 VM,直到它调用 DetachCurrentThread() 分离自身。

Updated link。另一个答案中的链接已过时。

就 Java 对象、类和方法 ID 而言,如果您要缓存它们,则需要 obtain a global reference。见What is 'JNI Global reference'Caching JNI objects and thread-safety (in Android)

【讨论】:

  • 谢谢,我不知道。我已经修改了代码,所以现在该结构只包含一个指向 JVM 的指针和一个对该对象的全局引用。每次我从“纯 C 上下文”调用 JNI 时,我之前都会调用 AttachCurrentThread。我仍然得到一个错误:JNIEnv *env; jint rs = (*jvm.jvm)->AttachCurrentThread(jvm.jvm, &env, NULL); assert(rs == JNI_OK); rs = (*jvm.jvm)->DetachCurrentThread(jvm.jvm); assert (rs == JNI_OK); ,断言是由对DetachCurrentThread的调用触发的...
  • “纯 C 上下文”是什么意思?根据Java API documentation:“附加到 VM 的本机线程必须调用 DetachCurrentThread() 以在退出之前分离自身。如果调用堆栈上有 Java 方法,则线程无法自行分离。”您可能不需要致电DetachCurrentThread()。在启动插件加载的初始 JNI 函数中打印出env 的值,然后在调用DetachCurrentThread() 的位置再次打印。可能是一样的,因为它是同一个线程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-12
  • 2011-01-26
  • 2015-01-28
  • 2014-03-30
  • 2010-11-14
相关资源
最近更新 更多