Una pieza de código C/C++ podría proporcionar un método JNI con una matriz de punteros de función. Pero, ¿hay alguna manera de llamar a la pila las funciones a las que apuntan los punteros de la matriz, directamente desde el interior del código Java (sin usar JNI o similar)? JNI de alguna manera hace algo así, por lo que debe haber una manera. ¿Cómo lo hace JNI? ¿Es a través de sun.misc.Unsafe? Incluso si no lo es, ¿podríamos usar alguna solución no segura para tener en nuestras manos el código JVM que hace eso?
No planeo usar eso comercialmente, por supuesto. Ni siquiera soy un profesional, simplemente disfruto de la codificación y he estado estudiando CUDA últimamente, así que pensé que tal vez podría experimentar mezclando todo, pero la sobrecarga de las llamadas JNI frustraría el propósito de tener un código acelerado por GPU.
¿JNI es tan lento?
JNI ya se ha optimizado mucho, deberías probarlo primero. Pero efectivamente tiene cierta sobrecarga, ver detalles.
Esta sobrecarga puede ser significativa si una función nativa es simple y se llama con frecuencia. JDK tiene una API privada llamada Nativos críticos para reducir la sobrecarga de las funciones de llamada que no requieren mucha funcionalidad JNI.
Nativos críticos
Un método nativo debe cumplir las siguientes condiciones para convertirse en un método nativo crítico:
- debe ser estático y no sincronizado;
- los tipos de argumento deben ser primitivo o arreglos primitivos;
- la implementación no debe llamar a funciones JNI, es decir, no puede asignar objetos Java ni generar excepciones;
- no debe funcionar durante mucho tiempo, ya que bloqueará GC mientras corre.
La declaración de un nativo crítico parece un método JNI regular, excepto que
- Empieza con
JavaCritical_
en lugar de Java_
;
- no tiene extra
JNIEnv*
y jclass
argumentos;
- Las matrices de Java se pasan en dos argumentos: el primero es una longitud de matriz y el segundo es un puntero a datos de matriz sin procesar. Es decir, no hay necesidad de llamar
GetArrayElements
y amigos, puede usar instantáneamente un puntero de matriz directo.
Por ejemplo, un método JNI
JNIEXPORT jint JNICALL
Java_com_package_MyClass_nativeMethod(JNIEnv* env, jclass klass, jbyteArray array) {
jboolean isCopy;
jint length = (*env)->GetArrayLength(env, array);
jbyte* buf = (*env)->GetByteArrayElements(env, array, &isCopy);
jint result = process(buf, length);
(*env)->ReleaseByteArrayElements(env, array, buf, JNI_ABORT);
return result;
}
se convertirá en
JNIEXPORT jint JNICALL
JavaCritical_com_package_MyClass_nativeMethod(jint length, jbyte* buf) {
return process(buf, length);
}
Los nativos críticos solo se admiten en HotSpot JVM a partir de JDK 7. Además, la versión “crítica” solo se llama desde el código compilado. Por lo tanto, necesita una implementación crítica y estándar para que esto funcione correctamente.
Esta característica fue diseñada para uso interno en JDK. No hay ninguna especificación pública o algo así. Probablemente la única documentación que puede encontrar es en los comentarios a JDK-7013347.
Punto de referencia
Este punto de referencia muestra que los nativos críticos pueden ser varias veces más rápidos que los métodos regulares de JNI cuando la carga de trabajo nativa es muy pequeña. Cuanto más largo es el método, menor es la sobrecarga relativa.

PD Hay un trabajo en curso en JDK para implementar Native MethodHandles que servirán como una alternativa más rápida a JNI. Sin embargo, es poco probable que aparezca antes de JDK 10.
- http://cr.openjdk.java.net/~jrose/panama/native-call-primitive.html
- http://mail.openjdk.java.net/pipermail/panama-dev/2015-December/000225.html
Vale la pena mencionar aquí que otra JVM popular de código abierto tiene un parecido, documentadopero no es una forma popular de acelerar las llamadas JNI para algunos métodos nativos.
Las llamadas nativas más rápidas a la interfaz nativa de Java (JNI) están disponibles mediante el @FastNative y @CriticalNative anotaciones Estas optimizaciones de tiempo de ejecución de ART incorporadas aceleran las transiciones JNI y reemplazan la notación !bang JNI ahora obsoleta. Las anotaciones no tienen efecto en los métodos no nativos y solo están disponibles para el código del lenguaje Java de la plataforma en bootclasspath (sin actualizaciones de Play Store).
los @FastNative La anotación admite métodos no estáticos. Use esto si un método accede a un proyecto de trabajo como parámetro o valor de retorno.
los @CriticalNative La anotación proporciona una forma aún más rápida de ejecutar métodos nativos, con las siguientes restricciones:
- Los métodos deben ser estáticos, sin objetos para parámetros, valores devueltos o un this implícito.
- Solo los tipos primitivos se pasan al método nativo.
- El método nativo no usa los parámetros JNIEnv y jclass en su definición de función.
- El método debe estar registrado con RegisterNatives en lugar de confiar en la vinculación dinámica de JNI.
los @FastNative y @CriticalNative las anotaciones deshabilitan la recolección de elementos no utilizados al ejecutar un método nativo. No lo use con métodos de ejecución prolongada, incluidos los métodos generalmente rápidos, pero generalmente ilimitados.
Las pausas en la recolección de elementos no utilizados pueden provocar interbloqueos. No adquiera bloqueos durante una llamada nativa rápida si los bloqueos no se han liberado localmente (es decir, antes de volver al código administrado). Esto no se aplica a las llamadas regulares de JNI, ya que ART considera que la ejecución del código nativo está suspendida.
@FastNative puede mejorar el rendimiento del método nativo hasta 3 veces, y @CriticalNative hasta 5x.
Este documento hace referencia al ahora obsoleto !estallido notación que se utilizó para acelerar algunas llamadas nativas en Dalvik JVM.
JNI lo hace al estar integrado en la JVM.
– usuario253751
30 de marzo de 2016 a las 1:42