SlideShare una empresa de Scribd logo
1 de 22
LIBRERÍAS NATIVAS

Una de las aplicaciones de la JNI es escribir métodos nativos que aprovechan códigoexistentes en
las bibliotecas nativas. Un enfoque típico, cubierto en este capítulo, es la producción deuna
biblioteca de clase que ajusta un conjunto de funciones nativas.

En este primer capítulo se analiza la forma más sencilla de escribir envolturaclases-uno-a-uno.
Luego introducimos una técnica, talones compartidos, quesimplifica la tarea de escribir clases
contenedoras.

Uno-a-uno y recibos comunes son técnicas para envolver nativofunciones. Al final de este capítulo,
también vamos a discutir la forma de envolver los datos nativosestructuras mediante las clases de
pares.

Los enfoques descritos en este capítulo se exponen directamente a una librería nativa
utilizandométodos nativos, y por lo tanto tienen la desventaja de hacer una aplicación que
llamatales métodos nativos que dependen de la biblioteca nativa. Tal solicitud puede ejecutarsólo
en un sistema operativo que suministra la biblioteca nativa. Una aproximación preferidaes
declarar independiente del sistema operativo métodos nativos. Sólo las funciones nativasla
aplicación de esos métodos nativos utilizan las bibliotecas nativas directamente, limitandola
necesidad de portar a las funciones nativas. La aplicación, incluyendo eldeclaraciones de métodos
nativos, no necesita ser portado.




9.1 Uno-a-uno

Empecemos con un ejemplo sencillo. Supongamos que queremos escribir un contenedorclase que
expone la función atol en la biblioteca estándar de C:

long atol(const char *str);




La función atol analiza una cadena y devuelve el valor decimal representadopor la cadena. Quizás
haya pocas razones para definir un método nativo en la práctica porque el método
Integer.parseInt, parte de la API Java, suministra lafuncionalidad equivalente. Evaluación de atol
("100"), por ejemplo, los resultados en el enterovalor 100. Definimos una clase contenedora de la
siguiente manera:

public class C {
public static native int atol(String str);
...
}
En aras de ilustrar la programación de JNI en C + +, implementaremosmétodos nativos en este
capítulo usando C + + (§ 8.6). El C + + aplicación de laC.atol método nativo es el siguiente:

JNIEXPORT jint JNICALL
Java_C_atol(JNIEnv *env, jclass cls, jstring str)
{
const char *cstr = env->GetStringUTFChars(str, 0);
if (cstr == NULL) {
return 0; /* out of memory */
}
int result = atol(cstr);
env->ReleaseStringUTFChars(str, cstr);
return result;
}




La aplicación es muy sencillo. Utilizamos GetStringUTFChars aconvertir la cadena Unicode porque
los números decimales son caracteres ASCII.

Veamos ahora un ejemplo más complejo que involucra la estructura pasapunteros a una función
de C. Supongamos que queremos escribir una clase contenedora queexpone la función CreateFile
de la API Win32:



typedef void * HANDLE;
typedef long DWORD;
typedef struct {...} SECURITY_ATTRIBUTES;
HANDLE CreateFile(
const char *fileName, // file name
DWORD desiredAccess, // access (read-write) mode
DWORD shareMode, // share mode
SECURITY_ATTRIBUTES *attrs, // security attributes
DWORD creationDistribution, // how to create
DWORD flagsAndAttributes, // file attributes
HANDLE templateFile // file with attr. to copy
);




La función CreateFile es compatible con una serie de características específicas de Win32
nodisponible en la API de archivos independiente de la plataforma Java. Por ejemplo, la función
Create-File puede ser utilizado para especificar los modos especiales de acceso y los atributos de
archivo aabrir las tuberías Win32 con nombre y para manejar las comunicaciones del puerto serie.



No vamos a discutir más detalles sobre la función CreateFile en este libro.
La atención se centrará en cómo CreateFile se pueden asignar a un método nativo definidoen una
clase de contenedor llamado Win32:



public class Win32 {
public static native int CreateFile(
String fileName, // file name
int desiredAccess, // access (read-write) mode
int shareMode, // share mode
int[] secAttrs, // security attributes
int creationDistribution, // how to create
int flagsAndAttributes, // file attributes
int templateFile); // file with attr. to copy
...
}




El mapeo del tipo de puntero char a String es obvio. Mapeamos elnativo Win32 tipo long (DWORD)
a int en el lenguaje de programación Java. El Win32 tipo HANDLE, un opaco 32-bit tipo de puntero,
también se asigna a int.

Debido a las posibles diferencias en cómo los campos están expuestos en la memoria, lo que
hacemosno del mapa estructuras C a clases en el lenguaje de programación Java. En su lugar, se
utilizauna matriz para almacenar los contenidos de los SECURITY_ATTRIBUTES estructura C. La
persona que llama

También puede pasar null como secAttrs para especificar la configuración predeterminada de
Win32 atributos de seguridad.

No vamos a discutir el contenido de la estructura SECURITY_ATTRIBUTES o cómoque codificar en
una matriz int.

Un C + + implementación del método nativo anterior es como sigue:



JNIEXPORT jint JNICALL Java_Win32_CreateFile(
JNIEnv *env,
jclass cls,
jstring fileName, // file name
jint desiredAccess, // access (read-write) mode
jint shareMode, // share mode
jintArray secAttrs, // security attributes
jint creationDistribution, // how to create
jint flagsAndAttributes, // file attributes
jint templateFile) // file with attr. to copy
{
jint result = 0;
jint *cSecAttrs = NULL;
if (secAttrs) {
cSecAttrs = env->GetIntArrayElements(secAttrs, 0);
if (cSecAttrs == NULL) {
return 0; /* out of memory */
}
}



char *cFileName = JNU_GetStringNativeChars(env, fileName);
if (cFileName) {
/* call the real Win32 function */
result = (jint)CreateFile(cFileName,
desiredAccess,
shareMode,
(SECURITY_ATTRIBUTES *)cSecAttrs,
creationDistribution,
flagsAndAttributes,
(HANDLE)templateFile);
free(cFileName);
}
/* else fall through, out of memory exception thrown */
if (secAttrs) {
env->ReleaseIntArrayElements(secAttrs, cSecAttrs, 0);
}
return result;
}




En primer lugar, convertir los atributos de seguridad almacenado en la matriz en una matriz de int
jint. Si el argumento es una referencia secAttrs NULL, se pasa NULL como el atributo de seguridad
a la función CreateFile de Win32. A continuación, hacemos un llamado a los
JNU_GetStringNativeChars función de utilidad (§ 8.2.2) para obtener el nombre de archivo
representado como una cadena de configuración regional específica C. Una vez que se han
convertido los atributos de seguridad y el nombre del archivo, pasamos los resultados de las
conversiones y el resto de argumentos a la función CreateFile de Win32.

Nosotros nos encargamos de comprobar las excepciones y liberar los recursos de la máquina
virtual(como cSecAttrs).

Los ejemplos C.atol y Win32.CreateFile demostrar un enfoque común a la escritura de clases
contenedoras y los métodos nativos. Cada función nativa (porejemplo, CreateFile) se asigna a una
función stub nativo único (por ejemplo,Java_Win32_CreateFile), que a su vez los mapas a una
definición de método nativo único(por ejemplo, Win32.CreateFile). En uno-a-uno, el talón de
funciónsirve para dos propósitos:

1. El talón se adapta convención la función nativa de paso de argumentos a lo que esesperado por
la máquina virtual de Java. La máquina virtual espera que el nativoimplementación del método a
seguir una convención de nombres dado y aceptar dosargumentos adicionales (el puntero JNIEnv y
el puntero "this").

2. El talón de conversión entre tipos de programación Java y los tipos nativos.
Por ejemplo, la función Java_Win32_CreateFile traduce la jstringnombre de archivo en una cadena
específica de la configuración regional C.



9.2 Stubs compartidas

El enfoque de uno a uno de mapeo requiere escribir una función derivada paracada función nativa
que desee ajustar. Esto se convierte en tedioso cuando se enfrentancon la tarea de escribir clases
de envoltura para un gran número de funciones nativas. Enesta sección se introduce el concepto
de recibos comunes y demostrar cómo compartirtalones se puede utilizar para simplificar la tarea
de escribir clases contenedoras.

Un talón compartido es un método nativo que se distribuye a otras funciones nativas. Lastub
compartido es responsable de convertir los tipos de argumentos a partir de lo que se
proporcionapor la persona que llama lo que es aceptado por las funciones nativas.

Pronto presentaremos un CFunction código auxiliar de clase compartida, pero primero vamos a
mostrarcómo puede simplificar la implementación del método C.atol:

public class C {
private static CFunction c_atol =
new CFunction("msvcrt.dll", // native library name
"atol", // C function name
"C"); // calling convention
public static int atol(String str) {
return c_atol.callInt(new Object[] {str});
}
...
}




C.atol ya no es un método nativo (y por lo tanto ya no se necesita una función stub).

En cambio, C.atol se define mediante la clase CFunction. La clase CFunctioninternamente
implementa un trozo compartida. El C.c_atol variable estática almacena una CFunctionobjeto que
corresponde a la función atol C en la biblioteca msvcrt.dll(la biblioteca C multihebra en Win32). El
constructor CFunction llamar tambiénespecifica que atol sigue la convención de llamada de C (§
11.4). Una vez que el c_atolcampo se inicializa, las llamadas al método C.atol sólo necesitan la
reexpedición a través dec_atol.callInt, el talón compartida.

La clase CFunction pertenece a una jerarquía de clases que vamos a construir yutilizar en breve:
Las instancias de la clase CFunction denotar un puntero a una función de C. CFunctiones una
subclase de CPointer, que denota punteros arbitrarias C:



public class CFunction extends CPointer {
public CFunction(String lib, // native library name
String fname, // C function name
String conv) { // calling convention
...
}
public native int callInt(Object[] args);
...
}




El método callInt toma como argumento una matriz de java.lang.Object. Loinspecciona los tipos de
los elementos de la matriz, los convierte (de jstring achar *, por ejemplo), y los pasa como
argumentos a la función de C subyacente.

El método callInt entonces devuelve el resultado de la función subyacente C comoint. La clase
CFunction puede definir métodos como callFloat o callDoublepara manejar las funciones de C con
tipos de retorno.

La clase CPointer se define como sigue:


public abstract class CPointer {
public native void copyIn(
int bOff, // offset from a C pointer
int[] buf, // source data
int off, // offset into source
int len); // number of elements to be copied
public native void copyOut(...);
...
}
CPointer es una clase abstracta que permite el acceso arbitrario a los punteros de C. La copyin
método, por ejemplo, copia un número de elementos de una matriz a intla memoria apuntada por
el puntero de C. Este método debe usarse con cuidadoya que se puede utilizar fácilmente para
corromper ubicaciones arbitrarias de memoria en la direcciónespacio. Los métodos nativos como
CPointer.copyIn son tan malas condiciones como puntero directomanipulación en C.

CMalloc es una subclase de CPointer que apunta a un bloque de memoria asignadaen el montón
de C usando malloc:

public class CMalloc extends CPointer {
public CMalloc(int size) throws OutOfMemoryError { ... }
public native void free();
...
}




El constructor CMalloc asigna un bloque de memoria del tamaño indicado en el Cmontón. El
método CMalloc.free libera el bloque de memoria.

Equipado con las clases CFunction y CMalloc, podemos reimplementarWin32.CreateFile como
sigue:



public class Win32 {
private static CFunction c_CreateFile =
new CFunction ("kernel32.dll", // native library name
"CreateFileA", // native function
"JNI"); // calling convention
public static int CreateFile(
String fileName, // file name
int desiredAccess, // access (read-write) mode
int shareMode, // share mode
int[] secAttrs, // security attributes
int creationDistribution, // how to create
int flagsAndAttributes, // file attributes
int templateFile) // file with attr. to copy
{
CMalloc cSecAttrs = null;
if (secAttrs != null) {
cSecAttrs = new CMalloc(secAttrs.length * 4);
cSecAttrs.copyIn(0, secAttrs, 0, secAttrs.length);
}
try {
return c_CreateFile.callInt(new Object[] {
fileName,
new Integer(desiredAccess),
new Integer(shareMode),
cSecAttrs,
new Integer(creationDistribution),
new Integer(flagsAndAttributes),
new Integer(templateFile)});
} finally {
if (secAttrs != null) {
cSecAttrs.free();
}
}
}
...
}




Estamos en caché el objeto CFunction en una variable estática. La API de Win32 Create-

El archivo se exporta desde kernel32.dll como CreateFileA. Otra entrada exportado,CreateFileW,
toma una cadena Unicode como el argumento de nombre de archivo. Esta función sigue la
convención JNI de llamada, que es la convención estándar de Win32 llamando(stdcall).

La aplicación Win32.CreateFile primero asigna un bloque de memoria en laC montón que es lo
suficientemente grande para contener los atributos de seguridad temporal. A continuación, los
paquetestodos los argumentos de una matriz y llama a la función subyacente C Create-FILEA a
través de la operadora compartida. Finalmente, el método Win32.CreateFile liberael bloque de
memoria C se utiliza para mantener los atributos de seguridad. Llamamos cSecAttrs.freeen una
cláusula finally para asegurarse de que la memoria temporal C se libera incluso si elllamada
c_CreateFile.callInt lanza una excepción.



9.3 Uno-a-uno frente a Stubs compartidas

Uno-a-uno y talones compartidos son dos maneras de crear clases contenedoraspara las
bibliotecas nativas. Cada uno tiene sus propias ventajas.

La principal ventaja de talones compartidos es que el programador no necesita escribir unagran
número de funciones auxiliares en código nativo. Una vez que una aplicación compartida stubtales
como CFunction está disponible, el programador puede ser capaz de construir envolturaclases sin
necesidad de escribir una sola línea de código nativo.

Talones compartidas deben usarse con cuidado, sin embargo. Con compartidos talones,
programadores

son esencialmente escribir código C en el lenguaje de programación Java. Estaderrota a la
seguridad de tipos del lenguaje de programación Java. Los errores en el uso detalones
compartidos puede conducir a la memoria dañada y aplicación se bloquea.

La ventaja de uno-a-uno, es que normalmente es más eficaz enla conversión de los tipos de datos
que se transfieren entre la máquina virtual Java ycódigo nativo. Talones compartidas, por otro
lado, puede manejar un máximo predeterminadoconjunto de tipos de argumentos y no se puede
lograr un rendimiento óptimo incluso para estostipos de argumentos. El llamador de
CFunction.callInt siempre tiene que crear un Integerobjeto para cada argumento int. Esto se suma
a la vez sobrecarga de espacio y tiempo a lacompartido esquema talones.

En la práctica, es necesario equilibrar el rendimiento, portabilidad y productividad a corto plazo.

Talones compartidos puede ser adecuado para aprovechar intrínsecamente no portátilescódigo
nativo que puede tolerar una ligera degradación del rendimiento, mientras que uno-a-unomapeo
se debe utilizar en los casos en que el máximo rendimiento es necesario o cuandomateria de
portabilidad.




9.4 Aplicación de Stubs compartidas

Tenemos CFunction tratado hasta ahora, CPointer, y clases CMalloc como cajas negras.

En esta sección se describe la forma en que se puede implementar utilizando las funciones básicas
de JNI.



9.4.1 La Clase CPointer

Nos fijamos en la clase CPointer primero porque es la superclase de ambos CFunctiony CMalloc. El
CPointer clase abstracta contiene un campo de 64 bits, los compañeros, que almacenael puntero
subyacente C:

public abstract class CPointer {
protected long peer;
public native void copyIn(int bOff, int[] buf,
int off,int len);
public native void copyOut(...);
...
}




La implementación en C + + de métodos nativos como copyin es sencillo:

JNIEXPORT void JNICALL
Java_CPointer_copyIn__I_3III(JNIEnv *env, jobject self,
jint boff, jintArray arr, jint off, jint len)
{
long peer = env->GetLongField(self, FID_CPointer_peer);
env->GetIntArrayRegion(arr, off, len, (jint *)peer + boff);
}
FID_CPointer_peer es el ID de campo calculado previamente para CPointer.peer. La
implementación del método nativo utiliza el esquema de codificación de nombre largo (§ 11,3)
aresolver los conflictos con las implementaciones de los métodos sobrecargados copyin nativas
paraotros tipos de matriz en la clase CPointer.



9.4.2 La Clase CMalloc

La clase CMalloc añade dos métodos nativos utilizados para asignar y liberar memoria Cbloques:

public class CMalloc extends CPointer {
private static native long malloc(int size);
public CMalloc(int size) throws OutOfMemoryError {
peer = malloc(size);
if (peer == 0) {
throw new OutOfMemoryError();
}
}
public native void free();
...
}




El constructor CMalloc llama a un método nativo CMalloc.malloc, y lanzaOutOfMemoryError
CMalloc.malloc si no vuelve a la memoria recién asignadabloquear C en el montón. Podemos
implementar la CMalloc.malloc y CMalloc.métodos libres de la siguiente manera:

JNIEXPORT jlong JNICALL
Java_CMalloc_malloc(JNIEnv *env, jclass cls, jint size)
{
return (jlong)malloc(size);
}
JNIEXPORT void JNICALL
Java_CMalloc_free(JNIEnv *env, jobject self)
{
long peer = env->GetLongField(self, FID_CPointer_peer);
free((void *)peer);
}




9.4.3 La Clase CFunction

La implementación de la clase CFunction requiere el uso de soporte dinámico que uneen el
sistema operativo, así como específico de la CPU código ensamblador. La implementaciónse
presenta a continuación está dirigida específicamente hacia el medio ambiente Win32/Intel x86.
Una vez que entienda los principios detrás de la aplicación de la CFunctionclase, puede seguir los
mismos pasos para ponerla en práctica en otras plataformas.La clase CFunction se define como
sigue:



public class CFunction extends CPointer {
private static final int CONV_C = 0;
private static final int CONV_JNI = 1;
private int conv;
private native long find(String lib, String fname);
public CFunction(String lib, // native library name
String fname, // C function name
String conv) { // calling convention
if (conv.equals("C")) {
conv = CONV_C;
} else if (conv.equals("JNI")) {
conv = CONV_JNI;
} else {
throw new IllegalArgumentException(
"bad calling convention");
}
peer = find(lib, fname);
}

public native int callInt(Object[] args);
...
}




La clase CFunction declara una conv campo privado utiliza para almacenar el llamadoconvención
de la función C. El método nativo CFunction.find se implementacomo sigue:

JNIEXPORT jlong JNICALL
Java_CFunction_find(JNIEnv *env, jobject self, jstring lib,
jstring fun)
{
void *handle;
void *func;
char *libname;
char *funname;
if ((libname = JNU_GetStringNativeChars(env, lib))) {
if ((funname = JNU_GetStringNativeChars(env, fun))) {
if ((handle = LoadLibrary(libname))) {
if (!(func = GetProcAddress(handle, funname))) {
JNU_ThrowByName(env,
"java/lang/UnsatisfiedLinkError",
funname);
}
} else {
JNU_ThrowByName(env,
"java/lang/UnsatisfiedLinkError",
libname);
}
free(funname);
}
free(libname);
}
return (jlong)func;
}




CFunction.find convierte el nombre de la biblioteca y el nombre de la función para la configuración
regional específicaCadenas de C, y luego llama a la API de Win32 y funciones
LoadLibraryGetProcAddress para localizar la función C en la biblioteca llamada nativa.

El método callInt, aplicada como sigue, lleva a cabo la tarea principal deredistribución de la carga a
la función subyacente C:

JNIEXPORT jint JNICALL
Java_CFunction_callInt(JNIEnv *env, jobject self,
jobjectArray arr)
{
#define MAX_NARGS 32
jint ires;
int nargs, nwords;
jboolean is_string[MAX_NARGS];
word_t args[MAX_NARGS];
nargs = env->GetArrayLength(arr);
if (nargs > MAX_NARGS) {
JNU_ThrowByName(env,
"java/lang/IllegalArgumentException",
"too many arguments");
return 0;
}
// convert arguments
for (nwords = 0; nwords < nargs; nwords++) {
is_string[nwords] = JNI_FALSE;
jobject arg = env->GetObjectArrayElement(arr, nwords);
if (arg == NULL) {
args[nwords].p = NULL;
} else if (env->IsInstanceOf(arg, Class_Integer)) {
args[nwords].i =
env->GetIntField(arg, FID_Integer_value);
} else if (env->IsInstanceOf(arg, Class_Float)) {
args[nwords].f =
env->GetFloatField(arg, FID_Float_value);
} else if (env->IsInstanceOf(arg, Class_CPointer)) {
args[nwords].p = (void *)
env->GetLongField(arg, FID_CPointer_peer);
} else if (env->IsInstanceOf(arg, Class_String)) {
char * cstr =
JNU_GetStringNativeChars(env, (jstring)arg);
if ((args[nwords].p = cstr) == NULL) {
goto cleanup; // error thrown
}
is_string[nwords] = JNI_TRUE;
} else {
JNU_ThrowByName(env,
"java/lang/IllegalArgumentException",
"unrecognized argument type");
goto cleanup;
}
env->DeleteLocalRef(arg);
}
void *func =
(void *)env->GetLongField(self, FID_CPointer_peer);
int conv = env->GetIntField(self, FID_CFunction_conv);
// now transfer control to func.
ires = asm_dispatch(func, nwords, args, conv);
cleanup:
// free all the native strings we have created
for (int i = 0; i < nwords; i++) {
if (is_string[i]) {
free(args[i].p);
}
}
return ires;
}




Suponemos que hemos puesto en marcha una serie de variables globales para almacenar en caché
elreferencias apropiadas de clase e identificadores de campo. Por ejemplo, variable
globalFID_CPointer_peer almacena el identificador de campo para CPointer.peer y variable
globalClass_String es una referencia mundial para el objeto de la clase java.lang.String. La tipo
word_t representa una palabra máquina y se define como sigue:



typedef union {
jint i;
jfloat f;
void *p;
} word_t;




La función Java_CFunction_callInt recorre en iteración la matriz de argumentos,

y comprueba el tipo de cada elemento:

• Si el elemento es una referencia nula, se pasa como un puntero NULL a la función C.

• Si el elemento es una instancia de la clase java.lang.Integer, el enterovalor se recupera y pasa a
la función C.

• Si el elemento es una instancia de la clase java.lang.Float, el punto flotantevalor se recupera y
pasa a la función C.

• Si el elemento es una instancia de la clase CPointer, el puntero del par se recuperay pasa a la
función C.

• Si el argumento es una instancia de java.lang.String, se convierte en unespecífico de la localidad
cadena C y se pasan a la función C.
• De lo contrario, una IllegalArgumentException es lanzada.



Nos revise cuidadosamente los posibles errores durante la conversión argumento y libretodo el
almacenamiento temporal asignado para cadenas de C antes de volver delJava_CFunction_callInt
función.

El código que transfiere los argumentos de los argumentos de búfer temporal para la Cfunción
necesita manipular la pila C directamente. Está escrito en ensamblador inline:


int asm_dispatch(void *func, // pointer to the C function
int nwords, // number of words in args array
word_t *args, // start of the argument data
int conv) // calling convention 0: C
// 1: JNI
{
__asm {
mov esi, args
mov edx, nwords
// word address -> byte address
shl edx, 2
sub edx, 4
jc args_done
// push the last argument first
args_loop:
mov eax, DWORD PTR [esi+edx]
push eax
sub edx, 4
jge SHORT args_loop
args_done:
call func
// check for calling convention
mov edx, conv
or edx, edx
jnz jni_call
// pop the arguments
mov edx, nwords
shl edx, 2
add esp, edx
jni_call:
// done, return value in eax
}
}




La rutina de ensamblado copia los argumentos en la pila C, entonces redespachosa la función func
C. Devolución funciones, la rutina comprueba asm_dispatch convención de llamada de función. Si
func sigue la convención de llamada de C,asm_dispatch hace estallar los argumentos pasados a
func. Si func sigue la llamada JNIconvención, asm_dispatch no salta los argumentos, los
argumentos de función apareceantes de que vuelva.
9.5 Clases de pares

Uno-a-uno y talones de ambos compartidos aborden el problema de envolverfunciones nativas.
También se encontró con el problema de envolver estructuras de datos nativasen el curso de la
construcción de la aplicación talones compartida. Recordemos ladefinición de la clase CPointer:



public abstract class CPointer {
protected long peer;
public native void copyIn(int bOff, int[] buf,
int off, int len);
public native void copyOut(...);
...
}




Contiene un campo de par 64-bit que se refiere a la estructura de datos original (en estecaso, una
parte de memoria en el espacio de direcciones C). Las subclases de CPointer asignarsignificados
específicos en el campo de pares. La clase CMalloc, por ejemplo, utiliza elcompañeros campo para
que apunte a un trozo de memoria en el montón de C:




Las clases que se corresponden directamente con las estructuras de datos nativas, como CPointery
CMalloc, se llaman clases de pares. Usted puede construir clases peer para una variedadde
estructuras de datos nativas, incluyendo, por ejemplo:

• descriptores de fichero

• descriptores de socket

• ventanas u otros gráficos de interfaz de usuario componentes




9.5.1 Clases de pares en la Plataforma Java
La corriente de JDK y versiones del SDK de Java 2 (1,1 y 1,2) utilizar las clases de pares internospara
implementar el java.io, java.net y paquetes java.awt. Una instancia de lajava.io.FileDescriptor
clase, por ejemplo, contiene un campo privado que representa fdun descriptor de fichero nativo:



// Implementation of the java.io.FileDescriptor class
public final class FileDescriptor {
private int fd;
...
}




Supongamos que usted desea llevar a cabo una operación de archivo que no está respaldada por
laPlataforma Java API. Usted puede tener la tentación de utilizar la JNI para averiguar el
subyacentedescriptor de archivo nativo de una instancia java.io.FileDescriptor. El JNI permitepara
acceder a un campo privado, siempre y cuando usted sabe su nombre y el tipo. Se podría
pensarque podría llevar a cabo la operación de archivo nativo directamente en el descriptor de
archivo.

Este enfoque, sin embargo, tiene un par de problemas:

• En primer lugar, usted está confiando en la aplicación java.io.FileDescriptor quealmacena el
descriptor de archivo nativo en un campo privado llamado fd. No hay garantía,sin embargo, que
las implementaciones futuras implementaciones de Sun o de otros fabricantesde la clase
java.io.FileDescriptor seguirá usando el mismo privadocampo fd nombre para el descriptor de
archivo nativo. El código nativo que asume el nombredel campo de los compañeros puede no
funcionar con una implementación diferente de la Javaplataforma.

• En segundo lugar, la operación se realiza directamente en el descriptor de archivo nativo
puedealterar la consistencia interna de la clase peer. Por ejemplo,casos java.io.FileDescriptor
mantener un estado interno que indicasi el descriptor de archivo nativo subyacente ha sido
cerrado. Si utiliza nativocódigo para omitir la clase peer y cerrar el descriptor de fichero
subyacente, el estadomantenido en el ejemplo java.io.FileDescriptor ya no será consistentecon el
verdadero estado del descriptor de archivo nativo. Peer implementaciones de la clasesuelen
asumir que tienen acceso exclusivo a la nativa subyacentela estructura de datos.

La única manera de superar estos problemas es definir sus propias clases peerque las estructuras
de envoltura de datos nativos. En el caso anterior, se puede definir su propio archivodescriptor de
la clase peer que soporta el conjunto necesario de las operaciones. Este enfoqueno te permite
usar tus propias clases peer para implementar clases de Java API. Usted no puede, por ejemplo,
pasar su propia instancia descriptor de fichero a un método queespera una instancia
java.io.FileDescriptor. Puede, sin embargo, fácilmente definirsu propia clase peer que implementa
una interfaz estándar de la API de Java. Esta es unafuerte argumento para diseñar APIs basados en
interfaces en lugar de clases.




9.5.2 Estructuras de liberar a los nativos de Datos

Clases de pares se definen en el lenguaje de programación Java, por lo que los casos de
compañeroslas clases serán basura recogida automáticamente. Usted necesita asegurarse de que,
sin embargo,que las estructuras subyacentes de datos nativos será liberado también.

Recordemos que la clase contiene un método CMalloc libre para liberar explícitamentemalloc'ed la
memoria C:



public class CMalloc extends CPointer {
public native void free();
...
}




Usted debe recordar llamar gratis a instancias de la clase CMalloc, de lo contrarioun ejemplo
CMalloc puede ser basura recogida, pero su correspondiente malloc'ed Cmemoria nunca se
recuperó.

Algunos programadores gusta poner un finalizador en clases peer, como CMalloc:



public class CMalloc extends CPointer {
public native synchronized void free();
protected void finalize() {
free();
}
...
}




La máquina virtual llama al método finalize antes de que la basura se acumula uninstancia de
CMalloc. Incluso si usted se olvida de llamar libre, el método finalice liberamalloc'ed la memoria C
para ti.

Es necesario hacer un pequeño cambio en la implementación del método nativo CMalloc.freepara
tener en cuenta la posibilidad de que pueda ser llamado varias veces. UstedTambién es necesario
que CMalloc.free un método sincronizado para evitar raza hilocondiciones:
JNIEXPORT void JNICALL
Java_CMalloc_free(JNIEnv *env, jobject self)
{
long peer = env->GetLongField(self, FID_CPointer_peer);
if (peer == 0) {
return; /* not an error, freed previously */
}
free((void *)peer);
peer = 0;
env->SetLongField(self, FID_CPointer_peer, peer);
}




Hemos establecido el campo par con dos declaraciones:

peer = 0;
env->SetLongField(self, FID_CPointer_peer, peer);


en lugar de una declaración:

env->SetLongField(self, FID_CPointer_peer, 0);




porque compiladores C + + se consideran el 0 literal como un entero de 32-bit, en lugar de un

64-bit entero. Algunos compiladores C + + permiten especificar 64-bit literales enteros,pero el uso
de 64-bit literales no será tan portátil.

Definición de un método de finalizar es una garantía adecuada, pero nunca debe dependeren los
finalizadores como único medio de liberar a las estructuras de datos nativas. La razón es quelas
estructuras de datos nativas pueden consumir muchos más recursos que sus paresinstancias. La
máquina virtual de Java no puede recoger la basura y finalizar instanciasde clases peer
suficientemente rápido para liberar a sus compañeros nativos.

Definición de un finalizador tiene consecuencias de rendimiento. Es típicamentemás lento para
crear y recuperar instancias de clases con finalizadores de crear yrecuperar los que no tienen
finalizadores.

Si usted siempre puede asegurarse de que liberar manualmente la estructura de datos nativa
paraclases peer, no es necesario definir un finalizador. Usted debe asegurarse, sin embargo,sin
estructuras de datos nativas en todos los caminos de la ejecución, de lo contrario puede haber
creadouna pérdida de recursos. Preste especial atención a las posibles excepciones lanzadas
duranteel proceso de utilizar una instancia de pares. Siempre estructuras nativas libres de datos
en unafinalmente cláusula:

CMalloc cptr = new CMalloc(10);
try {
... // use cptr
} finally {
cptr.free();
}




La cláusula finally asegura que cptr se libera incluso si se produce una excepcióndentro del bloque
try.




9.5.3 Backpointers to Peer instancias

Hemos demostrado que las clases de pares contienen típicamente un campo privado que se
refiere a lasubyacente estructura de datos original. En algunos casos es deseable incluir también
una referenciade la estructura de datos nativa de las instancias de la clase peer. Esto sucede,por
ejemplo, cuando el código nativo tiene que iniciar devoluciones de llamada a los métodos de
instanciaen la clase de pares.

Supongamos que estamos construyendo un componente de interfaz de usuario hipotética
llamadaKeyInput. KeyInput nativo de C + + componente, key_input, recibe un evento como
unKEY_PRESSED C + + llamada de función del sistema operativo cuando el usuario presionauna
tecla. El key_input C + + componente reporta el evento del sistema operativo para laKeyInput
instancia llamando al método keyPressed en la instancia KeyInput.

Las flechas en la figura a continuación indica cómo un evento de pulsación de tecla se origina por
unaprensa de usuario y clave propaga desde el key_input C + + componente al-Key. Entrada
instancia archivos:
La clase peer KeyInput se define como sigue:

class KeyInput {
private long peer;
private native long create();
private native void destroy(long peer);
public KeyInput() {
peer = create();
}
public destroy() {
destroy(peer);
}
private void keyPressed(int key) {
... /* process the key event */
}
}




La implementación del método nativo asigna a crear una instancia de la C + +estructura key_input.
C + + estructuras son similares a clases C + +, con la única diferenciaes que todos los miembros son
por defecto público en lugar de privado. Utilizamosuna estructura de C + + en lugar de una clase
de C + + en este ejemplo principalmente para evitar confusionescon clases en el lenguaje de
programación Java.



// C++ structure, native counterpart of KeyInput
struct key_input {
jobject back_ptr; // back pointer to peer instance
int key_pressed(int key); // called by the operating system
};
JNIEXPORT jlong JNICALL
Java_KeyInput_create(JNIEnv *env, jobject self)
{
key_input *cpp_obj = new key_input();
cpp_obj->back_ptr = env->NewGlobalRef(self);
return (jlong)cpp_obj;
}
JNIEXPORT void JNICALL
Java_KeyInput_destroy(JNIEnv *env, jobject self, jlong peer)
{
key_input *cpp_obj = (key_input*)peer;
env->DeleteGlobalRef(cpp_obj->back_ptr);
delete cpp_obj;
return;
}




El método create nativo asigna el C + + y se inicializa la estructura de suback_ptr campo a una
referencia global a la instancia de par KeyInput. La destruirelimina métodos nativos en la
referencia mundial para la instancia de pares y el C + estructura +que hace referencia el ejemplo
de pares. El constructor llama al crear KeyInputmétodo nativo para establecer la relación entre
una instancia de pares y su contraparte nativa:
Cuando el usuario pulsa una tecla, el sistema operativo llama al miembro de C + +función
key_input :: KEY_PRESSED. Esta función miembro responde a eventos porla emisión de una
devolución de llamada al método keyPressed en la instancia de par KeyInput.



// returns 0 on success, -1 on failure
int key_input::key_pressed(int key)
{
jboolean has_exception;
JNIEnv *env = JNU_GetEnv();
JNU_CallMethodByName(env,
&has_exception,
java_peer,
"keyPressed",
"()V",
key);
if (has_exception) {
env->ExceptionClear();
return -1;
} else {
return 0;
}
}




La función miembro key_press limpia cualquier excepción después de la devolución de llamada
yDevoluciones Condiciones de error en el sistema operativo con el código de retorno -1. referirse
aSecciones 6.2.3 y 8.4.1 para las definiciones de JNU_CallMethodByName yJNU_GetEnv funciones
de utilidad respectivamente.

Vamos a discutir un tema final antes de concluir esta sección. Supongamos que ustedañadir un
método de finalizar la clase KeyInput para evitar posibles fugas de memoria:


class KeyInput {
...
public synchronized destroy() {
if (peer != 0) {
destroy(peer);
peer = 0;
}
}
protect void finalize() {
destroy();
}
}




 El método destroy comprueba si el campo de pares es cero, y establece los parescampo a cero después de
llamar al método sobrecargado destruir nativo. Se define como unsincronizado método para evitar las
condiciones de carrera.



El código anterior no funcionará como es de esperar, sin embargo. El virtualmáquina nunca será recoger la
basura cualquier instancia KeyInput a menos que llamedestruir explícitamente. El constructor KeyInput crea
una referencia JNI globalla instancia KeyInput. La referencia mundial impide la instancia de KeyInputsiendo
recolector de basura. Puede superar este problema mediante el uso de un débil mundialreferencia en lugar de
una referencia global:



JNIEXPORT jlong JNICALL
Java_KeyInput_create(JNIEnv *env, jobject self)
{
key_input *cpp_obj = new key_input();
cpp_obj->back_ptr = env->NewWeakGlobalRef(self);
return (jlong)cpp_obj;
}
JNIEXPORT void JNICALL
Java_KeyInput_destroy(JNIEnv *env, jobject self, jlong peer)
{
key_input *cpp_obj = (key_input*)peer;
env->DeleteWeakGlobalRef(cpp_obj->back_ptr);
delete cpp_obj;
return;
}

Más contenido relacionado

La actualidad más candente

Javascript Módulo 7 - PROFUNDIZACIÓN EN ARRAYS, OBJETOS, PROPIEDADES, MÉTODOS...
Javascript Módulo 7 - PROFUNDIZACIÓN EN ARRAYS, OBJETOS, PROPIEDADES, MÉTODOS...Javascript Módulo 7 - PROFUNDIZACIÓN EN ARRAYS, OBJETOS, PROPIEDADES, MÉTODOS...
Javascript Módulo 7 - PROFUNDIZACIÓN EN ARRAYS, OBJETOS, PROPIEDADES, MÉTODOS...David Zapateria Besteiro
 
Jyoc java-cap10 clases complementarias y enumerados
Jyoc java-cap10 clases complementarias y enumeradosJyoc java-cap10 clases complementarias y enumerados
Jyoc java-cap10 clases complementarias y enumeradosJyoc X
 
Javascript Módulo 5 - Bucles, Arrays. Funciones como objectos. Ámbitos. Cierr...
Javascript Módulo 5 - Bucles, Arrays. Funciones como objectos. Ámbitos. Cierr...Javascript Módulo 5 - Bucles, Arrays. Funciones como objectos. Ámbitos. Cierr...
Javascript Módulo 5 - Bucles, Arrays. Funciones como objectos. Ámbitos. Cierr...David Zapateria Besteiro
 
P2_Actividad 4 :Glosario de términos
P2_Actividad 4 :Glosario de términosP2_Actividad 4 :Glosario de términos
P2_Actividad 4 :Glosario de términosJavier Leon
 
IntroduccióN A Visual C
IntroduccióN A Visual CIntroduccióN A Visual C
IntroduccióN A Visual Coswchavez
 
Java clases dictadas
Java clases dictadasJava clases dictadas
Java clases dictadasGaloGabriel
 
Introducción a la programación y la informática. Tema 4
Introducción a la programación y la informática. Tema 4Introducción a la programación y la informática. Tema 4
Introducción a la programación y la informática. Tema 4Andres Garcia Garcia
 
Presentación Java Evolution - GlobalLogic Club
Presentación Java Evolution - GlobalLogic ClubPresentación Java Evolution - GlobalLogic Club
Presentación Java Evolution - GlobalLogic ClubGlobalLogic Latinoamérica
 
Cuestionario
CuestionarioCuestionario
Cuestionariojpauly4
 
Lenguaje de Programacion - Java
Lenguaje de Programacion - JavaLenguaje de Programacion - Java
Lenguaje de Programacion - Javazousbabastre
 

La actualidad más candente (20)

Javascript Módulo 7 - PROFUNDIZACIÓN EN ARRAYS, OBJETOS, PROPIEDADES, MÉTODOS...
Javascript Módulo 7 - PROFUNDIZACIÓN EN ARRAYS, OBJETOS, PROPIEDADES, MÉTODOS...Javascript Módulo 7 - PROFUNDIZACIÓN EN ARRAYS, OBJETOS, PROPIEDADES, MÉTODOS...
Javascript Módulo 7 - PROFUNDIZACIÓN EN ARRAYS, OBJETOS, PROPIEDADES, MÉTODOS...
 
Jyoc java-cap10 clases complementarias y enumerados
Jyoc java-cap10 clases complementarias y enumeradosJyoc java-cap10 clases complementarias y enumerados
Jyoc java-cap10 clases complementarias y enumerados
 
Javascript Módulo 5 - Bucles, Arrays. Funciones como objectos. Ámbitos. Cierr...
Javascript Módulo 5 - Bucles, Arrays. Funciones como objectos. Ámbitos. Cierr...Javascript Módulo 5 - Bucles, Arrays. Funciones como objectos. Ámbitos. Cierr...
Javascript Módulo 5 - Bucles, Arrays. Funciones como objectos. Ámbitos. Cierr...
 
Multitarea e hilos en java con ejemplos
Multitarea e hilos en java con ejemplosMultitarea e hilos en java con ejemplos
Multitarea e hilos en java con ejemplos
 
P2_Actividad 4 :Glosario de términos
P2_Actividad 4 :Glosario de términosP2_Actividad 4 :Glosario de términos
P2_Actividad 4 :Glosario de términos
 
IntroduccióN A Visual C
IntroduccióN A Visual CIntroduccióN A Visual C
IntroduccióN A Visual C
 
Comandos Java
Comandos JavaComandos Java
Comandos Java
 
Documento Margarita
Documento MargaritaDocumento Margarita
Documento Margarita
 
Comandos de Java
Comandos de JavaComandos de Java
Comandos de Java
 
Programación III (Java) - 08 threads
Programación III (Java) - 08 threadsProgramación III (Java) - 08 threads
Programación III (Java) - 08 threads
 
Java clases dictadas
Java clases dictadasJava clases dictadas
Java clases dictadas
 
Threads en Java
Threads en JavaThreads en Java
Threads en Java
 
Constructores en Java
Constructores en JavaConstructores en Java
Constructores en Java
 
Introducción a la programación y la informática. Tema 4
Introducción a la programación y la informática. Tema 4Introducción a la programación y la informática. Tema 4
Introducción a la programación y la informática. Tema 4
 
Presentación Java Evolution - GlobalLogic Club
Presentación Java Evolution - GlobalLogic ClubPresentación Java Evolution - GlobalLogic Club
Presentación Java Evolution - GlobalLogic Club
 
Grupo1
Grupo1Grupo1
Grupo1
 
Introduccion lenguaje java
Introduccion lenguaje javaIntroduccion lenguaje java
Introduccion lenguaje java
 
Cuestionario
CuestionarioCuestionario
Cuestionario
 
Lenguaje de Programacion - Java
Lenguaje de Programacion - JavaLenguaje de Programacion - Java
Lenguaje de Programacion - Java
 
Datos Previos
Datos PreviosDatos Previos
Datos Previos
 

Destacado (9)

Flickr
FlickrFlickr
Flickr
 
Introduccion fyol
Introduccion fyolIntroduccion fyol
Introduccion fyol
 
3 amcpap
3 amcpap3 amcpap
3 amcpap
 
Tutorial modulo azul, tarea facil
Tutorial modulo azul, tarea facilTutorial modulo azul, tarea facil
Tutorial modulo azul, tarea facil
 
Ejercicio1
Ejercicio1 Ejercicio1
Ejercicio1
 
Bond 2004 Swes Orientation 20091014
Bond 2004 Swes Orientation 20091014Bond 2004 Swes Orientation 20091014
Bond 2004 Swes Orientation 20091014
 
Presentacion camtasia diego
Presentacion camtasia diegoPresentacion camtasia diego
Presentacion camtasia diego
 
Buenos dias compañeros y profe
Buenos dias compañeros y profeBuenos dias compañeros y profe
Buenos dias compañeros y profe
 
Actividad a1 estar
Actividad a1 estarActividad a1 estar
Actividad a1 estar
 

Similar a Traduccion capitulo 9 (completo)

Similar a Traduccion capitulo 9 (completo) (20)

Librerías nativas
Librerías nativasLibrerías nativas
Librerías nativas
 
C++
C++C++
C++
 
Programación en c++
Programación en c++Programación en c++
Programación en c++
 
Cplus
CplusCplus
Cplus
 
Aprendiendo a usar NDK Android Day(castellano)
Aprendiendo a usar NDK Android Day(castellano)Aprendiendo a usar NDK Android Day(castellano)
Aprendiendo a usar NDK Android Day(castellano)
 
Find File Servlet DB
Find File Servlet DBFind File Servlet DB
Find File Servlet DB
 
manual de C#
manual de C#manual de C#
manual de C#
 
Manual c# 2
Manual c# 2Manual c# 2
Manual c# 2
 
Manual de c#
Manual de c#Manual de c#
Manual de c#
 
Explicacion de la Clase en Java-MySQL.pdf
Explicacion de la Clase en Java-MySQL.pdfExplicacion de la Clase en Java-MySQL.pdf
Explicacion de la Clase en Java-MySQL.pdf
 
Semana9 Vbr
Semana9 VbrSemana9 Vbr
Semana9 Vbr
 
Introducción-a-Microsoft.NET-[parte3]
Introducción-a-Microsoft.NET-[parte3]Introducción-a-Microsoft.NET-[parte3]
Introducción-a-Microsoft.NET-[parte3]
 
Lenguajes de Programación: Clases y objetos
Lenguajes de Programación: Clases y objetosLenguajes de Programación: Clases y objetos
Lenguajes de Programación: Clases y objetos
 
2introduccionallenguajejava 141109171757-conversion-gate02
2introduccionallenguajejava 141109171757-conversion-gate022introduccionallenguajejava 141109171757-conversion-gate02
2introduccionallenguajejava 141109171757-conversion-gate02
 
INTRODUCCION LENGUAJE JAVA
INTRODUCCION LENGUAJE JAVAINTRODUCCION LENGUAJE JAVA
INTRODUCCION LENGUAJE JAVA
 
2) introduccion al lenguaje java
2) introduccion al lenguaje java2) introduccion al lenguaje java
2) introduccion al lenguaje java
 
19 javascript servidor
19 javascript servidor19 javascript servidor
19 javascript servidor
 
Tipos de datos
Tipos de datosTipos de datos
Tipos de datos
 
Tema 9 aplicaciones de dos capas por gio
Tema 9   aplicaciones de dos capas por gioTema 9   aplicaciones de dos capas por gio
Tema 9 aplicaciones de dos capas por gio
 
P2C2 Introducción a JEE5
P2C2 Introducción a JEE5P2C2 Introducción a JEE5
P2C2 Introducción a JEE5
 

Traduccion capitulo 9 (completo)

  • 1. LIBRERÍAS NATIVAS Una de las aplicaciones de la JNI es escribir métodos nativos que aprovechan códigoexistentes en las bibliotecas nativas. Un enfoque típico, cubierto en este capítulo, es la producción deuna biblioteca de clase que ajusta un conjunto de funciones nativas. En este primer capítulo se analiza la forma más sencilla de escribir envolturaclases-uno-a-uno. Luego introducimos una técnica, talones compartidos, quesimplifica la tarea de escribir clases contenedoras. Uno-a-uno y recibos comunes son técnicas para envolver nativofunciones. Al final de este capítulo, también vamos a discutir la forma de envolver los datos nativosestructuras mediante las clases de pares. Los enfoques descritos en este capítulo se exponen directamente a una librería nativa utilizandométodos nativos, y por lo tanto tienen la desventaja de hacer una aplicación que llamatales métodos nativos que dependen de la biblioteca nativa. Tal solicitud puede ejecutarsólo en un sistema operativo que suministra la biblioteca nativa. Una aproximación preferidaes declarar independiente del sistema operativo métodos nativos. Sólo las funciones nativasla aplicación de esos métodos nativos utilizan las bibliotecas nativas directamente, limitandola necesidad de portar a las funciones nativas. La aplicación, incluyendo eldeclaraciones de métodos nativos, no necesita ser portado. 9.1 Uno-a-uno Empecemos con un ejemplo sencillo. Supongamos que queremos escribir un contenedorclase que expone la función atol en la biblioteca estándar de C: long atol(const char *str); La función atol analiza una cadena y devuelve el valor decimal representadopor la cadena. Quizás haya pocas razones para definir un método nativo en la práctica porque el método Integer.parseInt, parte de la API Java, suministra lafuncionalidad equivalente. Evaluación de atol ("100"), por ejemplo, los resultados en el enterovalor 100. Definimos una clase contenedora de la siguiente manera: public class C { public static native int atol(String str); ... }
  • 2. En aras de ilustrar la programación de JNI en C + +, implementaremosmétodos nativos en este capítulo usando C + + (§ 8.6). El C + + aplicación de laC.atol método nativo es el siguiente: JNIEXPORT jint JNICALL Java_C_atol(JNIEnv *env, jclass cls, jstring str) { const char *cstr = env->GetStringUTFChars(str, 0); if (cstr == NULL) { return 0; /* out of memory */ } int result = atol(cstr); env->ReleaseStringUTFChars(str, cstr); return result; } La aplicación es muy sencillo. Utilizamos GetStringUTFChars aconvertir la cadena Unicode porque los números decimales son caracteres ASCII. Veamos ahora un ejemplo más complejo que involucra la estructura pasapunteros a una función de C. Supongamos que queremos escribir una clase contenedora queexpone la función CreateFile de la API Win32: typedef void * HANDLE; typedef long DWORD; typedef struct {...} SECURITY_ATTRIBUTES; HANDLE CreateFile( const char *fileName, // file name DWORD desiredAccess, // access (read-write) mode DWORD shareMode, // share mode SECURITY_ATTRIBUTES *attrs, // security attributes DWORD creationDistribution, // how to create DWORD flagsAndAttributes, // file attributes HANDLE templateFile // file with attr. to copy ); La función CreateFile es compatible con una serie de características específicas de Win32 nodisponible en la API de archivos independiente de la plataforma Java. Por ejemplo, la función Create-File puede ser utilizado para especificar los modos especiales de acceso y los atributos de archivo aabrir las tuberías Win32 con nombre y para manejar las comunicaciones del puerto serie. No vamos a discutir más detalles sobre la función CreateFile en este libro.
  • 3. La atención se centrará en cómo CreateFile se pueden asignar a un método nativo definidoen una clase de contenedor llamado Win32: public class Win32 { public static native int CreateFile( String fileName, // file name int desiredAccess, // access (read-write) mode int shareMode, // share mode int[] secAttrs, // security attributes int creationDistribution, // how to create int flagsAndAttributes, // file attributes int templateFile); // file with attr. to copy ... } El mapeo del tipo de puntero char a String es obvio. Mapeamos elnativo Win32 tipo long (DWORD) a int en el lenguaje de programación Java. El Win32 tipo HANDLE, un opaco 32-bit tipo de puntero, también se asigna a int. Debido a las posibles diferencias en cómo los campos están expuestos en la memoria, lo que hacemosno del mapa estructuras C a clases en el lenguaje de programación Java. En su lugar, se utilizauna matriz para almacenar los contenidos de los SECURITY_ATTRIBUTES estructura C. La persona que llama También puede pasar null como secAttrs para especificar la configuración predeterminada de Win32 atributos de seguridad. No vamos a discutir el contenido de la estructura SECURITY_ATTRIBUTES o cómoque codificar en una matriz int. Un C + + implementación del método nativo anterior es como sigue: JNIEXPORT jint JNICALL Java_Win32_CreateFile( JNIEnv *env, jclass cls, jstring fileName, // file name jint desiredAccess, // access (read-write) mode jint shareMode, // share mode jintArray secAttrs, // security attributes jint creationDistribution, // how to create jint flagsAndAttributes, // file attributes jint templateFile) // file with attr. to copy { jint result = 0; jint *cSecAttrs = NULL; if (secAttrs) { cSecAttrs = env->GetIntArrayElements(secAttrs, 0); if (cSecAttrs == NULL) { return 0; /* out of memory */
  • 4. } } char *cFileName = JNU_GetStringNativeChars(env, fileName); if (cFileName) { /* call the real Win32 function */ result = (jint)CreateFile(cFileName, desiredAccess, shareMode, (SECURITY_ATTRIBUTES *)cSecAttrs, creationDistribution, flagsAndAttributes, (HANDLE)templateFile); free(cFileName); } /* else fall through, out of memory exception thrown */ if (secAttrs) { env->ReleaseIntArrayElements(secAttrs, cSecAttrs, 0); } return result; } En primer lugar, convertir los atributos de seguridad almacenado en la matriz en una matriz de int jint. Si el argumento es una referencia secAttrs NULL, se pasa NULL como el atributo de seguridad a la función CreateFile de Win32. A continuación, hacemos un llamado a los JNU_GetStringNativeChars función de utilidad (§ 8.2.2) para obtener el nombre de archivo representado como una cadena de configuración regional específica C. Una vez que se han convertido los atributos de seguridad y el nombre del archivo, pasamos los resultados de las conversiones y el resto de argumentos a la función CreateFile de Win32. Nosotros nos encargamos de comprobar las excepciones y liberar los recursos de la máquina virtual(como cSecAttrs). Los ejemplos C.atol y Win32.CreateFile demostrar un enfoque común a la escritura de clases contenedoras y los métodos nativos. Cada función nativa (porejemplo, CreateFile) se asigna a una función stub nativo único (por ejemplo,Java_Win32_CreateFile), que a su vez los mapas a una definición de método nativo único(por ejemplo, Win32.CreateFile). En uno-a-uno, el talón de funciónsirve para dos propósitos: 1. El talón se adapta convención la función nativa de paso de argumentos a lo que esesperado por la máquina virtual de Java. La máquina virtual espera que el nativoimplementación del método a seguir una convención de nombres dado y aceptar dosargumentos adicionales (el puntero JNIEnv y el puntero "this"). 2. El talón de conversión entre tipos de programación Java y los tipos nativos.
  • 5. Por ejemplo, la función Java_Win32_CreateFile traduce la jstringnombre de archivo en una cadena específica de la configuración regional C. 9.2 Stubs compartidas El enfoque de uno a uno de mapeo requiere escribir una función derivada paracada función nativa que desee ajustar. Esto se convierte en tedioso cuando se enfrentancon la tarea de escribir clases de envoltura para un gran número de funciones nativas. Enesta sección se introduce el concepto de recibos comunes y demostrar cómo compartirtalones se puede utilizar para simplificar la tarea de escribir clases contenedoras. Un talón compartido es un método nativo que se distribuye a otras funciones nativas. Lastub compartido es responsable de convertir los tipos de argumentos a partir de lo que se proporcionapor la persona que llama lo que es aceptado por las funciones nativas. Pronto presentaremos un CFunction código auxiliar de clase compartida, pero primero vamos a mostrarcómo puede simplificar la implementación del método C.atol: public class C { private static CFunction c_atol = new CFunction("msvcrt.dll", // native library name "atol", // C function name "C"); // calling convention public static int atol(String str) { return c_atol.callInt(new Object[] {str}); } ... } C.atol ya no es un método nativo (y por lo tanto ya no se necesita una función stub). En cambio, C.atol se define mediante la clase CFunction. La clase CFunctioninternamente implementa un trozo compartida. El C.c_atol variable estática almacena una CFunctionobjeto que corresponde a la función atol C en la biblioteca msvcrt.dll(la biblioteca C multihebra en Win32). El constructor CFunction llamar tambiénespecifica que atol sigue la convención de llamada de C (§ 11.4). Una vez que el c_atolcampo se inicializa, las llamadas al método C.atol sólo necesitan la reexpedición a través dec_atol.callInt, el talón compartida. La clase CFunction pertenece a una jerarquía de clases que vamos a construir yutilizar en breve:
  • 6. Las instancias de la clase CFunction denotar un puntero a una función de C. CFunctiones una subclase de CPointer, que denota punteros arbitrarias C: public class CFunction extends CPointer { public CFunction(String lib, // native library name String fname, // C function name String conv) { // calling convention ... } public native int callInt(Object[] args); ... } El método callInt toma como argumento una matriz de java.lang.Object. Loinspecciona los tipos de los elementos de la matriz, los convierte (de jstring achar *, por ejemplo), y los pasa como argumentos a la función de C subyacente. El método callInt entonces devuelve el resultado de la función subyacente C comoint. La clase CFunction puede definir métodos como callFloat o callDoublepara manejar las funciones de C con tipos de retorno. La clase CPointer se define como sigue: public abstract class CPointer { public native void copyIn( int bOff, // offset from a C pointer int[] buf, // source data int off, // offset into source int len); // number of elements to be copied public native void copyOut(...); ... }
  • 7. CPointer es una clase abstracta que permite el acceso arbitrario a los punteros de C. La copyin método, por ejemplo, copia un número de elementos de una matriz a intla memoria apuntada por el puntero de C. Este método debe usarse con cuidadoya que se puede utilizar fácilmente para corromper ubicaciones arbitrarias de memoria en la direcciónespacio. Los métodos nativos como CPointer.copyIn son tan malas condiciones como puntero directomanipulación en C. CMalloc es una subclase de CPointer que apunta a un bloque de memoria asignadaen el montón de C usando malloc: public class CMalloc extends CPointer { public CMalloc(int size) throws OutOfMemoryError { ... } public native void free(); ... } El constructor CMalloc asigna un bloque de memoria del tamaño indicado en el Cmontón. El método CMalloc.free libera el bloque de memoria. Equipado con las clases CFunction y CMalloc, podemos reimplementarWin32.CreateFile como sigue: public class Win32 { private static CFunction c_CreateFile = new CFunction ("kernel32.dll", // native library name "CreateFileA", // native function "JNI"); // calling convention public static int CreateFile( String fileName, // file name int desiredAccess, // access (read-write) mode int shareMode, // share mode int[] secAttrs, // security attributes int creationDistribution, // how to create int flagsAndAttributes, // file attributes int templateFile) // file with attr. to copy { CMalloc cSecAttrs = null; if (secAttrs != null) { cSecAttrs = new CMalloc(secAttrs.length * 4); cSecAttrs.copyIn(0, secAttrs, 0, secAttrs.length); } try { return c_CreateFile.callInt(new Object[] { fileName, new Integer(desiredAccess), new Integer(shareMode), cSecAttrs, new Integer(creationDistribution), new Integer(flagsAndAttributes), new Integer(templateFile)}); } finally { if (secAttrs != null) { cSecAttrs.free();
  • 8. } } } ... } Estamos en caché el objeto CFunction en una variable estática. La API de Win32 Create- El archivo se exporta desde kernel32.dll como CreateFileA. Otra entrada exportado,CreateFileW, toma una cadena Unicode como el argumento de nombre de archivo. Esta función sigue la convención JNI de llamada, que es la convención estándar de Win32 llamando(stdcall). La aplicación Win32.CreateFile primero asigna un bloque de memoria en laC montón que es lo suficientemente grande para contener los atributos de seguridad temporal. A continuación, los paquetestodos los argumentos de una matriz y llama a la función subyacente C Create-FILEA a través de la operadora compartida. Finalmente, el método Win32.CreateFile liberael bloque de memoria C se utiliza para mantener los atributos de seguridad. Llamamos cSecAttrs.freeen una cláusula finally para asegurarse de que la memoria temporal C se libera incluso si elllamada c_CreateFile.callInt lanza una excepción. 9.3 Uno-a-uno frente a Stubs compartidas Uno-a-uno y talones compartidos son dos maneras de crear clases contenedoraspara las bibliotecas nativas. Cada uno tiene sus propias ventajas. La principal ventaja de talones compartidos es que el programador no necesita escribir unagran número de funciones auxiliares en código nativo. Una vez que una aplicación compartida stubtales como CFunction está disponible, el programador puede ser capaz de construir envolturaclases sin necesidad de escribir una sola línea de código nativo. Talones compartidas deben usarse con cuidado, sin embargo. Con compartidos talones, programadores son esencialmente escribir código C en el lenguaje de programación Java. Estaderrota a la seguridad de tipos del lenguaje de programación Java. Los errores en el uso detalones compartidos puede conducir a la memoria dañada y aplicación se bloquea. La ventaja de uno-a-uno, es que normalmente es más eficaz enla conversión de los tipos de datos que se transfieren entre la máquina virtual Java ycódigo nativo. Talones compartidas, por otro lado, puede manejar un máximo predeterminadoconjunto de tipos de argumentos y no se puede lograr un rendimiento óptimo incluso para estostipos de argumentos. El llamador de
  • 9. CFunction.callInt siempre tiene que crear un Integerobjeto para cada argumento int. Esto se suma a la vez sobrecarga de espacio y tiempo a lacompartido esquema talones. En la práctica, es necesario equilibrar el rendimiento, portabilidad y productividad a corto plazo. Talones compartidos puede ser adecuado para aprovechar intrínsecamente no portátilescódigo nativo que puede tolerar una ligera degradación del rendimiento, mientras que uno-a-unomapeo se debe utilizar en los casos en que el máximo rendimiento es necesario o cuandomateria de portabilidad. 9.4 Aplicación de Stubs compartidas Tenemos CFunction tratado hasta ahora, CPointer, y clases CMalloc como cajas negras. En esta sección se describe la forma en que se puede implementar utilizando las funciones básicas de JNI. 9.4.1 La Clase CPointer Nos fijamos en la clase CPointer primero porque es la superclase de ambos CFunctiony CMalloc. El CPointer clase abstracta contiene un campo de 64 bits, los compañeros, que almacenael puntero subyacente C: public abstract class CPointer { protected long peer; public native void copyIn(int bOff, int[] buf, int off,int len); public native void copyOut(...); ... } La implementación en C + + de métodos nativos como copyin es sencillo: JNIEXPORT void JNICALL Java_CPointer_copyIn__I_3III(JNIEnv *env, jobject self, jint boff, jintArray arr, jint off, jint len) { long peer = env->GetLongField(self, FID_CPointer_peer); env->GetIntArrayRegion(arr, off, len, (jint *)peer + boff); }
  • 10. FID_CPointer_peer es el ID de campo calculado previamente para CPointer.peer. La implementación del método nativo utiliza el esquema de codificación de nombre largo (§ 11,3) aresolver los conflictos con las implementaciones de los métodos sobrecargados copyin nativas paraotros tipos de matriz en la clase CPointer. 9.4.2 La Clase CMalloc La clase CMalloc añade dos métodos nativos utilizados para asignar y liberar memoria Cbloques: public class CMalloc extends CPointer { private static native long malloc(int size); public CMalloc(int size) throws OutOfMemoryError { peer = malloc(size); if (peer == 0) { throw new OutOfMemoryError(); } } public native void free(); ... } El constructor CMalloc llama a un método nativo CMalloc.malloc, y lanzaOutOfMemoryError CMalloc.malloc si no vuelve a la memoria recién asignadabloquear C en el montón. Podemos implementar la CMalloc.malloc y CMalloc.métodos libres de la siguiente manera: JNIEXPORT jlong JNICALL Java_CMalloc_malloc(JNIEnv *env, jclass cls, jint size) { return (jlong)malloc(size); } JNIEXPORT void JNICALL Java_CMalloc_free(JNIEnv *env, jobject self) { long peer = env->GetLongField(self, FID_CPointer_peer); free((void *)peer); } 9.4.3 La Clase CFunction La implementación de la clase CFunction requiere el uso de soporte dinámico que uneen el sistema operativo, así como específico de la CPU código ensamblador. La implementaciónse presenta a continuación está dirigida específicamente hacia el medio ambiente Win32/Intel x86.
  • 11. Una vez que entienda los principios detrás de la aplicación de la CFunctionclase, puede seguir los mismos pasos para ponerla en práctica en otras plataformas.La clase CFunction se define como sigue: public class CFunction extends CPointer { private static final int CONV_C = 0; private static final int CONV_JNI = 1; private int conv; private native long find(String lib, String fname); public CFunction(String lib, // native library name String fname, // C function name String conv) { // calling convention if (conv.equals("C")) { conv = CONV_C; } else if (conv.equals("JNI")) { conv = CONV_JNI; } else { throw new IllegalArgumentException( "bad calling convention"); } peer = find(lib, fname); } public native int callInt(Object[] args); ... } La clase CFunction declara una conv campo privado utiliza para almacenar el llamadoconvención de la función C. El método nativo CFunction.find se implementacomo sigue: JNIEXPORT jlong JNICALL Java_CFunction_find(JNIEnv *env, jobject self, jstring lib, jstring fun) { void *handle; void *func; char *libname; char *funname; if ((libname = JNU_GetStringNativeChars(env, lib))) { if ((funname = JNU_GetStringNativeChars(env, fun))) { if ((handle = LoadLibrary(libname))) { if (!(func = GetProcAddress(handle, funname))) { JNU_ThrowByName(env, "java/lang/UnsatisfiedLinkError", funname); } } else { JNU_ThrowByName(env, "java/lang/UnsatisfiedLinkError", libname); } free(funname); } free(libname); }
  • 12. return (jlong)func; } CFunction.find convierte el nombre de la biblioteca y el nombre de la función para la configuración regional específicaCadenas de C, y luego llama a la API de Win32 y funciones LoadLibraryGetProcAddress para localizar la función C en la biblioteca llamada nativa. El método callInt, aplicada como sigue, lleva a cabo la tarea principal deredistribución de la carga a la función subyacente C: JNIEXPORT jint JNICALL Java_CFunction_callInt(JNIEnv *env, jobject self, jobjectArray arr) { #define MAX_NARGS 32 jint ires; int nargs, nwords; jboolean is_string[MAX_NARGS]; word_t args[MAX_NARGS]; nargs = env->GetArrayLength(arr); if (nargs > MAX_NARGS) { JNU_ThrowByName(env, "java/lang/IllegalArgumentException", "too many arguments"); return 0; } // convert arguments for (nwords = 0; nwords < nargs; nwords++) { is_string[nwords] = JNI_FALSE; jobject arg = env->GetObjectArrayElement(arr, nwords); if (arg == NULL) { args[nwords].p = NULL; } else if (env->IsInstanceOf(arg, Class_Integer)) { args[nwords].i = env->GetIntField(arg, FID_Integer_value); } else if (env->IsInstanceOf(arg, Class_Float)) { args[nwords].f = env->GetFloatField(arg, FID_Float_value); } else if (env->IsInstanceOf(arg, Class_CPointer)) { args[nwords].p = (void *) env->GetLongField(arg, FID_CPointer_peer); } else if (env->IsInstanceOf(arg, Class_String)) { char * cstr = JNU_GetStringNativeChars(env, (jstring)arg); if ((args[nwords].p = cstr) == NULL) { goto cleanup; // error thrown } is_string[nwords] = JNI_TRUE; } else { JNU_ThrowByName(env, "java/lang/IllegalArgumentException", "unrecognized argument type"); goto cleanup; } env->DeleteLocalRef(arg); }
  • 13. void *func = (void *)env->GetLongField(self, FID_CPointer_peer); int conv = env->GetIntField(self, FID_CFunction_conv); // now transfer control to func. ires = asm_dispatch(func, nwords, args, conv); cleanup: // free all the native strings we have created for (int i = 0; i < nwords; i++) { if (is_string[i]) { free(args[i].p); } } return ires; } Suponemos que hemos puesto en marcha una serie de variables globales para almacenar en caché elreferencias apropiadas de clase e identificadores de campo. Por ejemplo, variable globalFID_CPointer_peer almacena el identificador de campo para CPointer.peer y variable globalClass_String es una referencia mundial para el objeto de la clase java.lang.String. La tipo word_t representa una palabra máquina y se define como sigue: typedef union { jint i; jfloat f; void *p; } word_t; La función Java_CFunction_callInt recorre en iteración la matriz de argumentos, y comprueba el tipo de cada elemento: • Si el elemento es una referencia nula, se pasa como un puntero NULL a la función C. • Si el elemento es una instancia de la clase java.lang.Integer, el enterovalor se recupera y pasa a la función C. • Si el elemento es una instancia de la clase java.lang.Float, el punto flotantevalor se recupera y pasa a la función C. • Si el elemento es una instancia de la clase CPointer, el puntero del par se recuperay pasa a la función C. • Si el argumento es una instancia de java.lang.String, se convierte en unespecífico de la localidad cadena C y se pasan a la función C.
  • 14. • De lo contrario, una IllegalArgumentException es lanzada. Nos revise cuidadosamente los posibles errores durante la conversión argumento y libretodo el almacenamiento temporal asignado para cadenas de C antes de volver delJava_CFunction_callInt función. El código que transfiere los argumentos de los argumentos de búfer temporal para la Cfunción necesita manipular la pila C directamente. Está escrito en ensamblador inline: int asm_dispatch(void *func, // pointer to the C function int nwords, // number of words in args array word_t *args, // start of the argument data int conv) // calling convention 0: C // 1: JNI { __asm { mov esi, args mov edx, nwords // word address -> byte address shl edx, 2 sub edx, 4 jc args_done // push the last argument first args_loop: mov eax, DWORD PTR [esi+edx] push eax sub edx, 4 jge SHORT args_loop args_done: call func // check for calling convention mov edx, conv or edx, edx jnz jni_call // pop the arguments mov edx, nwords shl edx, 2 add esp, edx jni_call: // done, return value in eax } } La rutina de ensamblado copia los argumentos en la pila C, entonces redespachosa la función func C. Devolución funciones, la rutina comprueba asm_dispatch convención de llamada de función. Si func sigue la convención de llamada de C,asm_dispatch hace estallar los argumentos pasados a func. Si func sigue la llamada JNIconvención, asm_dispatch no salta los argumentos, los argumentos de función apareceantes de que vuelva.
  • 15. 9.5 Clases de pares Uno-a-uno y talones de ambos compartidos aborden el problema de envolverfunciones nativas. También se encontró con el problema de envolver estructuras de datos nativasen el curso de la construcción de la aplicación talones compartida. Recordemos ladefinición de la clase CPointer: public abstract class CPointer { protected long peer; public native void copyIn(int bOff, int[] buf, int off, int len); public native void copyOut(...); ... } Contiene un campo de par 64-bit que se refiere a la estructura de datos original (en estecaso, una parte de memoria en el espacio de direcciones C). Las subclases de CPointer asignarsignificados específicos en el campo de pares. La clase CMalloc, por ejemplo, utiliza elcompañeros campo para que apunte a un trozo de memoria en el montón de C: Las clases que se corresponden directamente con las estructuras de datos nativas, como CPointery CMalloc, se llaman clases de pares. Usted puede construir clases peer para una variedadde estructuras de datos nativas, incluyendo, por ejemplo: • descriptores de fichero • descriptores de socket • ventanas u otros gráficos de interfaz de usuario componentes 9.5.1 Clases de pares en la Plataforma Java
  • 16. La corriente de JDK y versiones del SDK de Java 2 (1,1 y 1,2) utilizar las clases de pares internospara implementar el java.io, java.net y paquetes java.awt. Una instancia de lajava.io.FileDescriptor clase, por ejemplo, contiene un campo privado que representa fdun descriptor de fichero nativo: // Implementation of the java.io.FileDescriptor class public final class FileDescriptor { private int fd; ... } Supongamos que usted desea llevar a cabo una operación de archivo que no está respaldada por laPlataforma Java API. Usted puede tener la tentación de utilizar la JNI para averiguar el subyacentedescriptor de archivo nativo de una instancia java.io.FileDescriptor. El JNI permitepara acceder a un campo privado, siempre y cuando usted sabe su nombre y el tipo. Se podría pensarque podría llevar a cabo la operación de archivo nativo directamente en el descriptor de archivo. Este enfoque, sin embargo, tiene un par de problemas: • En primer lugar, usted está confiando en la aplicación java.io.FileDescriptor quealmacena el descriptor de archivo nativo en un campo privado llamado fd. No hay garantía,sin embargo, que las implementaciones futuras implementaciones de Sun o de otros fabricantesde la clase java.io.FileDescriptor seguirá usando el mismo privadocampo fd nombre para el descriptor de archivo nativo. El código nativo que asume el nombredel campo de los compañeros puede no funcionar con una implementación diferente de la Javaplataforma. • En segundo lugar, la operación se realiza directamente en el descriptor de archivo nativo puedealterar la consistencia interna de la clase peer. Por ejemplo,casos java.io.FileDescriptor mantener un estado interno que indicasi el descriptor de archivo nativo subyacente ha sido cerrado. Si utiliza nativocódigo para omitir la clase peer y cerrar el descriptor de fichero subyacente, el estadomantenido en el ejemplo java.io.FileDescriptor ya no será consistentecon el verdadero estado del descriptor de archivo nativo. Peer implementaciones de la clasesuelen asumir que tienen acceso exclusivo a la nativa subyacentela estructura de datos. La única manera de superar estos problemas es definir sus propias clases peerque las estructuras de envoltura de datos nativos. En el caso anterior, se puede definir su propio archivodescriptor de la clase peer que soporta el conjunto necesario de las operaciones. Este enfoqueno te permite usar tus propias clases peer para implementar clases de Java API. Usted no puede, por ejemplo, pasar su propia instancia descriptor de fichero a un método queespera una instancia java.io.FileDescriptor. Puede, sin embargo, fácilmente definirsu propia clase peer que implementa
  • 17. una interfaz estándar de la API de Java. Esta es unafuerte argumento para diseñar APIs basados en interfaces en lugar de clases. 9.5.2 Estructuras de liberar a los nativos de Datos Clases de pares se definen en el lenguaje de programación Java, por lo que los casos de compañeroslas clases serán basura recogida automáticamente. Usted necesita asegurarse de que, sin embargo,que las estructuras subyacentes de datos nativos será liberado también. Recordemos que la clase contiene un método CMalloc libre para liberar explícitamentemalloc'ed la memoria C: public class CMalloc extends CPointer { public native void free(); ... } Usted debe recordar llamar gratis a instancias de la clase CMalloc, de lo contrarioun ejemplo CMalloc puede ser basura recogida, pero su correspondiente malloc'ed Cmemoria nunca se recuperó. Algunos programadores gusta poner un finalizador en clases peer, como CMalloc: public class CMalloc extends CPointer { public native synchronized void free(); protected void finalize() { free(); } ... } La máquina virtual llama al método finalize antes de que la basura se acumula uninstancia de CMalloc. Incluso si usted se olvida de llamar libre, el método finalice liberamalloc'ed la memoria C para ti. Es necesario hacer un pequeño cambio en la implementación del método nativo CMalloc.freepara tener en cuenta la posibilidad de que pueda ser llamado varias veces. UstedTambién es necesario que CMalloc.free un método sincronizado para evitar raza hilocondiciones:
  • 18. JNIEXPORT void JNICALL Java_CMalloc_free(JNIEnv *env, jobject self) { long peer = env->GetLongField(self, FID_CPointer_peer); if (peer == 0) { return; /* not an error, freed previously */ } free((void *)peer); peer = 0; env->SetLongField(self, FID_CPointer_peer, peer); } Hemos establecido el campo par con dos declaraciones: peer = 0; env->SetLongField(self, FID_CPointer_peer, peer); en lugar de una declaración: env->SetLongField(self, FID_CPointer_peer, 0); porque compiladores C + + se consideran el 0 literal como un entero de 32-bit, en lugar de un 64-bit entero. Algunos compiladores C + + permiten especificar 64-bit literales enteros,pero el uso de 64-bit literales no será tan portátil. Definición de un método de finalizar es una garantía adecuada, pero nunca debe dependeren los finalizadores como único medio de liberar a las estructuras de datos nativas. La razón es quelas estructuras de datos nativas pueden consumir muchos más recursos que sus paresinstancias. La máquina virtual de Java no puede recoger la basura y finalizar instanciasde clases peer suficientemente rápido para liberar a sus compañeros nativos. Definición de un finalizador tiene consecuencias de rendimiento. Es típicamentemás lento para crear y recuperar instancias de clases con finalizadores de crear yrecuperar los que no tienen finalizadores. Si usted siempre puede asegurarse de que liberar manualmente la estructura de datos nativa paraclases peer, no es necesario definir un finalizador. Usted debe asegurarse, sin embargo,sin estructuras de datos nativas en todos los caminos de la ejecución, de lo contrario puede haber creadouna pérdida de recursos. Preste especial atención a las posibles excepciones lanzadas
  • 19. duranteel proceso de utilizar una instancia de pares. Siempre estructuras nativas libres de datos en unafinalmente cláusula: CMalloc cptr = new CMalloc(10); try { ... // use cptr } finally { cptr.free(); } La cláusula finally asegura que cptr se libera incluso si se produce una excepcióndentro del bloque try. 9.5.3 Backpointers to Peer instancias Hemos demostrado que las clases de pares contienen típicamente un campo privado que se refiere a lasubyacente estructura de datos original. En algunos casos es deseable incluir también una referenciade la estructura de datos nativa de las instancias de la clase peer. Esto sucede,por ejemplo, cuando el código nativo tiene que iniciar devoluciones de llamada a los métodos de instanciaen la clase de pares. Supongamos que estamos construyendo un componente de interfaz de usuario hipotética llamadaKeyInput. KeyInput nativo de C + + componente, key_input, recibe un evento como unKEY_PRESSED C + + llamada de función del sistema operativo cuando el usuario presionauna tecla. El key_input C + + componente reporta el evento del sistema operativo para laKeyInput instancia llamando al método keyPressed en la instancia KeyInput. Las flechas en la figura a continuación indica cómo un evento de pulsación de tecla se origina por unaprensa de usuario y clave propaga desde el key_input C + + componente al-Key. Entrada instancia archivos:
  • 20. La clase peer KeyInput se define como sigue: class KeyInput { private long peer; private native long create(); private native void destroy(long peer); public KeyInput() { peer = create(); } public destroy() { destroy(peer); } private void keyPressed(int key) { ... /* process the key event */ } } La implementación del método nativo asigna a crear una instancia de la C + +estructura key_input. C + + estructuras son similares a clases C + +, con la única diferenciaes que todos los miembros son por defecto público en lugar de privado. Utilizamosuna estructura de C + + en lugar de una clase de C + + en este ejemplo principalmente para evitar confusionescon clases en el lenguaje de programación Java. // C++ structure, native counterpart of KeyInput struct key_input { jobject back_ptr; // back pointer to peer instance int key_pressed(int key); // called by the operating system }; JNIEXPORT jlong JNICALL Java_KeyInput_create(JNIEnv *env, jobject self) { key_input *cpp_obj = new key_input(); cpp_obj->back_ptr = env->NewGlobalRef(self); return (jlong)cpp_obj; } JNIEXPORT void JNICALL Java_KeyInput_destroy(JNIEnv *env, jobject self, jlong peer) { key_input *cpp_obj = (key_input*)peer; env->DeleteGlobalRef(cpp_obj->back_ptr); delete cpp_obj; return; } El método create nativo asigna el C + + y se inicializa la estructura de suback_ptr campo a una referencia global a la instancia de par KeyInput. La destruirelimina métodos nativos en la referencia mundial para la instancia de pares y el C + estructura +que hace referencia el ejemplo de pares. El constructor llama al crear KeyInputmétodo nativo para establecer la relación entre una instancia de pares y su contraparte nativa:
  • 21. Cuando el usuario pulsa una tecla, el sistema operativo llama al miembro de C + +función key_input :: KEY_PRESSED. Esta función miembro responde a eventos porla emisión de una devolución de llamada al método keyPressed en la instancia de par KeyInput. // returns 0 on success, -1 on failure int key_input::key_pressed(int key) { jboolean has_exception; JNIEnv *env = JNU_GetEnv(); JNU_CallMethodByName(env, &has_exception, java_peer, "keyPressed", "()V", key); if (has_exception) { env->ExceptionClear(); return -1; } else { return 0; } } La función miembro key_press limpia cualquier excepción después de la devolución de llamada yDevoluciones Condiciones de error en el sistema operativo con el código de retorno -1. referirse aSecciones 6.2.3 y 8.4.1 para las definiciones de JNU_CallMethodByName yJNU_GetEnv funciones de utilidad respectivamente. Vamos a discutir un tema final antes de concluir esta sección. Supongamos que ustedañadir un método de finalizar la clase KeyInput para evitar posibles fugas de memoria: class KeyInput { ... public synchronized destroy() { if (peer != 0) {
  • 22. destroy(peer); peer = 0; } } protect void finalize() { destroy(); } } El método destroy comprueba si el campo de pares es cero, y establece los parescampo a cero después de llamar al método sobrecargado destruir nativo. Se define como unsincronizado método para evitar las condiciones de carrera. El código anterior no funcionará como es de esperar, sin embargo. El virtualmáquina nunca será recoger la basura cualquier instancia KeyInput a menos que llamedestruir explícitamente. El constructor KeyInput crea una referencia JNI globalla instancia KeyInput. La referencia mundial impide la instancia de KeyInputsiendo recolector de basura. Puede superar este problema mediante el uso de un débil mundialreferencia en lugar de una referencia global: JNIEXPORT jlong JNICALL Java_KeyInput_create(JNIEnv *env, jobject self) { key_input *cpp_obj = new key_input(); cpp_obj->back_ptr = env->NewWeakGlobalRef(self); return (jlong)cpp_obj; } JNIEXPORT void JNICALL Java_KeyInput_destroy(JNIEnv *env, jobject self, jlong peer) { key_input *cpp_obj = (key_input*)peer; env->DeleteWeakGlobalRef(cpp_obj->back_ptr); delete cpp_obj; return; }