Это - вопрос, связанный с предыдущим сообщением, но это сообщение было решено, и теперь я хотел изменить направление вопроса.
При работе с JNI необходимо спросить JNIEnv
объект для jclass
и jmethodID
для каждого класса и метода, который будет использоваться в коде C/C++. Только, чтобы быть ясным, я хочу вызвать конструкторов Java или методы от C/C++.
Так как коммуникация от Java до C/C++ (и наоборот) является дорогостоящей, я первоначально думал, что один способ минимизировать это состоял в том, чтобы снова использовать jclass
и jmethodID
. Поэтому я сохранил это экземпляры в глобальных переменных следующим образом:
jclass someClass = NULL;
jmethodID someMethod = NULL;
JNIEXPORT jobject JNICALL Java_example_method1(JNIEnv *env, jobject jobj) {
// initialize someClass and someMethod if they are NULL
// use someClass and someMethod to call java (for example, thru NewObject)
}
JNIEXPORT jobject JNICALL Java_example_method2(JNIEnv *env, jobject jobj) {
// initialize someClass and someMethod if they are NULL
// use someClass and someMethod to call java again
}
Более определенное (и полезный) пример, который я использую для выдавания исключения отовсюду в моих функциях JNI:
jclass jniExceptionClass = NULL;
void throwJavaException(JNIEnv *env, const char* msg) {
if (!jniExceptionClass) {
jniExceptionClass = env->FindClass("example/JNIRuntimeException");
}
if (jniExceptionClass)
env->ThrowNew(jniExceptionClass, msg);
}
}
Проблема состоит в том, что я продолжил использовать этот шаблон и получил отказ сегментации, который был только решен не снова использованным это переменные (это было решением предыдущего сообщения).
Вопросы:
jclass
и jmethodID
через различные функции JNI? Я думал, что это оценивает, всегда было то же.jclass
и jmethodID
для каждой функции JNI?Правила здесь ясны. Значения идентификатора метода и идентификатора поля навсегда. Вы можете повесить на них. Поиски занимают некоторое время.
JClass
, с другой стороны, обычно является локальной ссылкой . Местная ссылка выживает максимум, продолжительность единого вызова функции JNI.
Если вам нужно оптимизировать, вы должны задать JVM, чтобы сделать глобальную ссылку для вас. Нередко приобретать и поддерживать ссылки на общие классы, такие как java.lang.string
.
Удерживание такой ссылки на класс предотвратит его (класс), конечно, собирали мусор.
jclass local = env->FindClass(CLS_JAVA_LANG_STRING);
_CHECK_JAVA_EXCEPTION(env);
java_lang_string_class = (jclass)env->NewGlobalRef(local);
_CHECK_JAVA_EXCEPTION(env);
env->DeleteLocalRef(local);
_CHECK_JAVA_EXCEPTION(env);
Проверка макроса звонков:
static inline void
check_java_exception(JNIEnv *env, int line)
{
UNUSED(line);
if(env->ExceptionOccurred()) {
#ifdef DEBUG
fprintf(stderr, "Java exception at rlpjni.cpp line %d\n", line);
env->ExceptionDescribe();
abort();
#endif
throw bt_rlpjni_java_is_upset();
}
}
Внутри jni_onload
, нужно использовать newglobalref
На значениях jclass
значений, возвращаемых FindClass
, прежде чем их кэшировать.
Затем внутри jni_onunload
Вы звоните DELETEGLOBALREF
.
В мире JDBC обычной практикой (в соответствии с JDBC API) является использование Class # forName ()
для загрузки драйвера JDBC. Драйвер JDBC должен зарегистрироваться в DriverManager
внутри статического блока:
package com.dbvendor.jdbc;
import java.sql.Driver;
import java.sql.DriverManager;
public class MyDriver implements Driver {
static {
DriverManager.registerDriver(new MyDriver());
}
public MyDriver() {
//
}
}
Вызов Class # forName ()
выполнит все статические инициализаторы . Это путь, что DriverManager
может найти соответствующий драйвер среди зарегистрированных драйверов по URL-адресу подключения во время getConnection ()
, который примерно выглядит следующим образом:
public static Connection getConnection(String url) throws SQLException {
for (Driver driver : registeredDrivers) {
if (driver.acceptsURL(url)) {
return driver.connect(url);
}
}
throw new SQLException("No suitable driver");
}
Но также были драйверы JDBC, начиная с хорошо известного примера org.gjt.mm.mysql.Driver
, который неправильно регистрируется внутри Constructor вместо статического блока:
package com.dbvendor.jdbc;
import java.sql.Driver;
import java.sql.DriverManager;
public class BadDriver implements Driver {
public BadDriver() {
DriverManager.registerDriver(this);
}
}
Единственный способ заставить его работать динамически - это вызвать newInstance ()
после этого! Иначе вы столкнетесь с первого взгляда необъяснимым «SQLException: нет подходящего драйвера». Опять же, это ошибка в драйвере JDBC, а не в вашем коде. В настоящее время ни один драйвер JDBC не должен содержать эту ошибку. Таким образом, вы можете (и должны) оставить newInstance ()
.
Есть JLexPHP: https://github.com/wez/JLexPHP/blob/master/jlex.php
Я его не использовал, но есть такой: http://pear.php.net/package/PHP_ParserGenerator , который создает PHP-анализатор из грамматики Lemon . Проект, кажется, неактивен.
Я также нашел этот проект: http://code.google.com/p/antlrphpruntime/ , в котором используется Antlr . Опять неактивно, хотя.
-121--1341508-Поскольку я помню, что jclass является локальным по отношению к вызывающему методу, его нельзя кэшировать, однако идентификатор метода может быть. Для получения дополнительной информации см. http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/design.html .
Извините, я не знаю про аспект производительности, каждый раз, когда я использовал JNI, он был незначительным по сравнению с задачей.