world reservada palabra nazareno hello descargar java jni

java - reservada - jni types



¿Por qué no debería volver a utilizar un jclass y/o jmethodID en JNI? (5)

Como otros ya escribieron

  1. Puede almacenar jmethodID en una variable estática de C ++ sin problemas
  2. Puede almacenar local jobject o jclass en una variable estática de C ++ después de convertirlos en objetos globales llamando a env->NewGloablRef()

Solo quiero agregar aquí una información adicional: la razón principal para almacenar jclass en una variable estática de C ++ será que usted cree que es un problema de rendimiento llamar a env->FindClass() cada vez.

Pero FindClass() la velocidad de FindClass() con un contador de rendimiento con la API QueryPerformanceCounter() en Windows. Y el resultado fue asombroso:

En mi computadora con una CPU de 3,6 GHz, la ejecución de

jcass p_Container = env->FindClass("java/awt/Container");

toma entre 0,01 ms y 0,02 ms. Eso es increíblemente rápido. Miré el código fuente de Java y usan un diccionario donde están almacenadas las clases. Esto parece implementarse de manera muy eficiente.

Probé algunas clases más y aquí está el resultado:

Elapsed 0.002061 ms for java/net/URL Elapsed 0.044390 ms for java/lang/Boolean Elapsed 0.019235 ms for java/lang/Character Elapsed 0.018372 ms for java/lang/Number Elapsed 0.017931 ms for java/lang/Byte Elapsed 0.017589 ms for java/lang/Short Elapsed 0.017371 ms for java/lang/Integer Elapsed 0.015637 ms for java/lang/Double Elapsed 0.018173 ms for java/lang/String Elapsed 0.015895 ms for java/math/BigDecimal Elapsed 0.016204 ms for java/awt/Rectangle Elapsed 0.016272 ms for java/awt/Point Elapsed 0.001817 ms for java/lang/Object Elapsed 0.016057 ms for java/lang/Class Elapsed 0.016829 ms for java/net/URLClassLoader Elapsed 0.017807 ms for java/lang/reflect/Field Elapsed 0.016658 ms for java/util/Locale Elapsed 0.015720 ms for java/lang/System Elapsed 0.014669 ms for javax/swing/JTable Elapsed 0.017276 ms for javax/swing/JComboBox Elapsed 0.014777 ms for javax/swing/JList Elapsed 0.015597 ms for java/awt/Component Elapsed 0.015223 ms for javax/swing/JComponent Elapsed 0.017385 ms for java/lang/Throwable Elapsed 0.015089 ms for java/lang/StackTraceElement

Los valores anteriores provienen de la cadena de distribución de eventos de Java. Si ejecuto el mismo código en un hilo de Windows nativo que ha sido creado por CreateThread() por mí, se ejecuta incluso 10 veces más rápido . ¿Por qué?

Por lo tanto, si no llama a FindClass() mucha frecuencia, no hay ningún problema para FindClass() cuando se llame a su función JNI en lugar de crear una referencia global y almacenarla en una variable estática.

Otro tema importante es la seguridad de hilos . En Java cada hilo tiene su propia estructura independiente JNIEnv .

  1. Global jobject o jclass son válidos en cualquier hilo de Java.
  2. Los objetos locales solo son válidos en una invocación de función en el JNIEnv del subproceso que realiza la llamada y son basura recopilada cuando el código JNI vuelve a Java.

Ahora depende de los hilos que está utilizando: si registra su función C ++ con env->RegisterNatives() y el código Java llama a sus funciones JNI, entonces debe almacenar todos los objetos, que quiera usar más adelante, como objetos globales de lo contrario, obtendrán basura recolectada.

Pero si crea su propio hilo con la API CraeteThread() (en Windows) y obtiene la estructura JNIEnv llamando a AttachCurrentThreadAsDaemon() entonces se aplicarán completamente otras reglas: como es su propio hilo, nunca devuelve el control a Java, la basura collector nunca limpiará los objetos que haya creado en su hilo, ¡e incluso podrá almacenar objetos locales en variables estáticas de C ++ sin problemas! (Pero no se puede acceder a estos desde otros subprocesos) En este caso, es extremadamente importante que limpie TODAS sus instancias locales manualmente con env->DeleteLocalRef() contrario, tendrá una pérdida de memoria.

Recomiendo encarecidamente que cargue TODOS los objetos locales en una clase contenedora que llame a DeleteLocalRef() en su destructor. Esta es una forma a prueba de balas para evitar fugas de memoria.

Desarrollar código JNI puede ser muy engorroso y puede obtener bloqueos que no comprende. Para encontrar la causa de un bloqueo, abra una ventana de DOS e inicie su aplicación Java con el siguiente comando:

java -Xcheck:jni -jar MyApplication.jar

entonces verás qué problemas han sucedido en el código JNI. Por ejemplo:

FATAL ERROR in native method: Bad global or local ref passed to JNI

y encontrará el stacktrace en el archivo de registro que Java crea en la misma carpeta donde tiene el archivo JAR:

# # A fatal error has been detected by the Java Runtime Environment: # # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x6e8655d5, pid=4692, tid=4428 # etc...

Esta es una pregunta relacionada con una publicación anterior , pero esta publicación se resolvió y ahora quería cambiar la dirección de la pregunta.

Cuando se trabaja con JNI, es necesario preguntar al objeto JNIEnv por jclass y jmethodID para cada clase y método que se usará en el código C / C ++. Para que quede claro, quiero llamar a los contructors de Java o a los métodos de C / C ++.

Como la comunicación de Java a C / C ++ (y viceversa) es costosa, inicialmente pensé que una forma de minimizar esto era reutilizar jclass y jmethodID . Por lo tanto, guardé estas instancias en variables globales de la siguiente manera:

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 }

Un ejemplo más específico (y útil), que utilizo para lanzar excepciones desde cualquier lugar en mis funciones JNI:

jclass jniExceptionClass = NULL; void throwJavaException(JNIEnv *env, const char* msg) { if (!jniExceptionClass) { jniExceptionClass = env->FindClass("example/JNIRuntimeException"); } if (jniExceptionClass) env->ThrowNew(jniExceptionClass, msg); } }

El problema es que continué usando este patrón y obtuve una falla de segmentación que solo fue resuelta al no reutilizar estas variables (esta fue la solución a la publicación anterior).

Las preguntas son:

  • ¿Por qué es ilegal reutilizar jclass y jmethodID través de diferentes funciones de JNI? Pensé que estos valores siempre eran los mismos.
  • Solo por curiosidad: ¿cuál es el impacto / sobrecarga de inicializar todos los jclass y jmethodID necesarios para cada función JNI?

Como recuerdo, jclass será local para el método de llamada, por lo que no se puede almacenar en caché, sin embargo, el ID del método puede ser. Consulte http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/design.html para obtener más información detallada.

Lo siento, no sé sobre el aspecto del rendimiento, cada vez que he usado JNI ha sido insignificante en comparación con la tarea en cuestión.


Dentro de JNI_OnLoad , debe usar NewGlobalRef en los valores de jclass devueltos por FindClass antes de almacenarlos en caché.

Luego, dentro de JNI_OnUnload usted llama DeleteGlobalRef en ellos.


Las reglas aquí son claras. Los ID de método y los valores de ID de campo son para siempre. Puedes aferrarte a ellos. Las búsquedas toman algo de tiempo.

jclass , por otro lado, es generalmente una referencia local . Una referencia local sobrevive, como máximo, a la duración de una sola llamada a una función JNI.

Si necesita optimizar, debe pedirle a la JVM que haga una referencia global por usted. No es raro adquirir y mantener referencias a clases comunes como java.lang.String .

La celebración de una referencia de este tipo a una clase evitará que (la clase) se recolecte basura, por supuesto.

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);

La macro de verificación llama:

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(); } }


Puede almacenar en caché y utilizar sus ID de método / función / clase y usarlos de forma segura si lo codifica de forma adecuada. He respondido una pregunta similar aquí en SO y he publicado un código explícito sobre cómo seguir las recomendaciones de rendimiento de caché de IBM.